在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)界面變成了這樣

而我想要的效果是這樣

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版本