解决在Flutter Row和Column的children中使

2021-04-24  本文已影响0人  諸星団

在Row或Column中可以使用crossAxisAlignment在交叉轴方向控制children的排列位置,假设在Row中把它设置成CrossAxisAlignment.start,即所有child都是靠上方排列对齐,但恰好有一个child需要居中对齐,首先想到的肯定是给这个child套了个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)),
    ],
)

但很遗憾,运行起来后发现界面变成了这样

1.1
而我想要的效果是这样
1.2
Row的高度应该是自适应的,取决于最高的那个子child(图1.2中间的那个蓝色子child),但很显然由于Align的加入直接将Row的高度干到了最大。

不过其实稍微一想图1.1的结果是有道理的,Align的确需要尺寸定位无限大才能对它的child进行布局,它的parent也当然必须有明确的尺寸约束,所以要做到这个效果就必须对Row的源码动手脚。

查看Row的父类Flex继承自RenderObjectWidget,发现其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;
    }
  }

这个方法代码非常的多,直接通读会很难受,但好在关键点的代码非常的好找,可以看到759行会循环所有子child进行测量,同时把最大的子child高度记录在crossSize中,接着在865行通过crossSize确定自己的高度。

所以关键点就在child.layout,我们可以重写这个过程,在计算crossSize的时候把Align排除出去,只计算非Align类型的child,这样crossSize记录的就是除去Align以外最高的child。
然后对Align类型的child重新测量,直接使用crossSize作为约束的maxHeight即可。
而对于非Align类型的child则需要重新布局,这是因为它们的offset已经被Align污染,这很简单,新的offset的width取旧值即可,height可以根据我们自己的crossSize重新计算,当然最后不要忘了把size中height换成我们自己计算的crossSize。

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


要达成这样的效果我们需要写一个继承自Row或Column的子类,非常简单,完整代码

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

上一篇下一篇

猜你喜欢

热点阅读