解決在Flutter Row和Column的children中使用Align或Center無(wú)效的問(wèn)題

在Row或Column中可以使用crossAxisAlignment在交叉軸方向控制children的排列位置,假設(shè)在Row中把它設(shè)置成CrossAxisAlignment.start,即所有child都是靠上方排列對(duì)齊,但恰好有一個(gè)child需要居中對(duì)齊,首先想到的肯定是給這個(gè)child套了個(gè)Center或Align,像這樣:

Row(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
            Container(width: 30, height: 30, color: Colors.amberAccent),
            Expanded(child: Container(height: 80, color: Colors.blueAccent)),
            Align(child: Container(width: 15, height: 15, color: Colors.deepOrange)),
    ],
)

但很遺憾,運(yùn)行起來(lái)后發(fā)現(xiàn)界面變成了這樣

1.1

而我想要的效果是這樣
1.2

Row的高度應(yīng)該是自適應(yīng)的,取決于最高的那個(gè)子child(圖1.2中間的那個(gè)藍(lán)色子child),但很顯然由于Align的加入直接將Row的高度干到了最大。

不過(guò)其實(shí)稍微一想圖1.1的結(jié)果是有道理的,Align的確需要尺寸定位無(wú)限大才能對(duì)它的child進(jìn)行布局,它的parent也當(dāng)然必須有明確的尺寸約束,所以要做到這個(gè)效果就必須對(duì)Row的源碼動(dòng)手腳。

查看Row的父類(lèi)Flex繼承自RenderObjectWidget,發(fā)現(xiàn)其createRenderObject方法返回的是RenderFlex,而RenderFlex繼承自RenderBox,RenderBox繼承自RenderObject

class Flex extends MultiChildRenderObjectWidget {
  ...
  @override
  RenderFlex createRenderObject(BuildContext context) {
    return RenderFlex(...);
  }
}
class RenderFlex extends RenderBox {...}
abstract class RenderBox extends RenderObject {...}

RenderObject的這一套我們就非常熟悉了,所以直接看RenderFlex的performLayout干了啥

//flex.dart 
@override
  void performLayout() {
    ...
    double crossSize = 0.0;
    ...
    while (child != null) {
        ...
          //759行
          switch (_direction) {
            case Axis.horizontal:
              innerConstraints = BoxConstraints(maxHeight: constraints.maxHeight);
              break;
          }
        child.layout(innerConstraints, parentUsesSize: true);
        crossSize = math.max(crossSize, _getCrossSize(child));
      }
    }
    ...
    //865行
    switch (_direction) {
      case Axis.horizontal:
        size = constraints.constrain(Size(idealSize, crossSize));
        break;
    }
  }

這個(gè)方法代碼非常的多,直接通讀會(huì)很難受,但好在關(guān)鍵點(diǎn)的代碼非常的好找,可以看到759行會(huì)循環(huán)所有子child進(jìn)行測(cè)量,同時(shí)把最大的子child高度記錄在crossSize中,接著在865行通過(guò)crossSize確定自己的高度。

所以關(guān)鍵點(diǎn)就在child.layout,我們可以重寫(xiě)這個(gè)過(guò)程,在計(jì)算crossSize的時(shí)候把Align排除出去,只計(jì)算非Align類(lèi)型的child,這樣crossSize記錄的就是除去Align以外最高的child。
然后對(duì)Align類(lèi)型的child重新測(cè)量,直接使用crossSize作為約束的maxHeight即可。
而對(duì)于非Align類(lèi)型的child則需要重新布局,這是因?yàn)樗鼈兊膐ffset已經(jīng)被Align污染,這很簡(jiǎn)單,新的offset的width取舊值即可,height可以根據(jù)我們自己的crossSize重新計(jì)算,當(dāng)然最后不要忘了把size中height換成我們自己計(jì)算的crossSize。

Axis.vertical方向的Column處理方式和以上同理。


要達(dá)成這樣的效果我們需要寫(xiě)一個(gè)繼承自Row或Column的子類(lèi),非常簡(jiǎn)單,完整代碼

ps:本文flex.dart源碼基于flutter 1.22.6版本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容