美文网首页
解决在Flutter Row和Column的children中使

解决在Flutter Row和Column的children中使

作者: 諸星団 | 来源:发表于2021-04-24 04:34 被阅读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版本

    相关文章

      网友评论

          本文标题:解决在Flutter Row和Column的children中使

          本文链接:https://www.haomeiwen.com/subject/wzfhrltx.html