Flutter布局——一段代码解释最常见的约束错误

flutter布局的原理

Constraints go down, Sizes go up, Parent sets position

  • 父节点向子节点传约束
  • 子节点向父节点上传大小
  • 最后由父节点决定位置

不是按照直接约束显示

问题代码

Scaffold(
      body: Center(
        child: ConstrainedBox(
          constraints: BoxConstraints.tight(const Size(300, 300)),
          child: ColoredBox(
            color: Colors.yellow,
            child: ConstrainedBox(
              constraints: BoxConstraints.tight(const Size(100, 200)),
              child: const ColoredBox(
                color: Colors.red,
                child: SizedBox(
                  width: 100,
                  height: 100,
                  child: ColoredBox(color: Colors.teal),
                ),
              ),
            ),
          ),
        ),
      ),
    );

显示效果

最终只显示了蓝绿色,而且没有按照我们对蓝绿色组件直接的约束大小显示。

错误分析
  • 这段代码,我们逐步移出蓝绿色、黄色代码,发现黄色和红色均显示为300*300
  • 我们想显示的蓝绿色、红色均没有按照我们直接的约束显示,都被黄色父节点约束覆盖了。
  • 而黄色可以理解为按照起父级约束显示。

我们发现黄色的父级约束盒子的父级是Center, 这个就是重点

  1. Center的继承关系是:SingleChildRenderObjectWidget>Align>Center
  2. ConstrainedBox的继承关系是:SingleChildRenderObjectWidget>ConstrainedBox
  3. SizedBox的继承关系是:SingleChildRenderObjectWidget>SizedBox

这里先插入一个知识,我们在屏幕上看到的UI都是通过RenderObjectWidget实现的。而RenderObjectWidget中有个creatRenderObject方法生成RenderObject对象,RenderObject实际负责layout()和paint()。

其中Align的creatRenderObject方法返回的是RenderPositionedBox;

SizedBoxConstrainedBox的creatRenderObject方法返回的是RenderConstrainedBox;

RenderPositionedBox 和 RenderConstrainedBox

RenderPositionedBoxRenderConstrainedBox 最终集成的都是RenderBox

我们先来对比一下用于计算布局的performLayout方法

RenderPositionedBox

这里在子节点渲染时,修改了布局约束,改为松约束。
其中constraints.loosen()是把min约束给删除,可以理解为置零

  @override
  void performLayout() {
    final BoxConstraints constraints = this.constraints;
    final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity;
    final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity;

    if (child != null) {
    // 这里在子节点渲染时,修改了布局约束,改为松约束。
    // 其中constraints.loosen()是把min约束给删除,可以理解为置零
      child!.layout(constraints.loosen(), parentUsesSize: true);
    // 根据子节点大小计算自己的大小
    // 默认是没设置Factor 那么自身size就是最大值double.infinity
      size = constraints.constrain(Size(
        shrinkWrapWidth ? child!.size.width * (_widthFactor ?? 1.0) : double.infinity,
        shrinkWrapHeight ? child!.size.height * (_heightFactor ?? 1.0) : double.infinity,
      ));
    // 根据自身size,子节点size,来绘制子节点位置
      alignChild();
    } else {
      size = constraints.constrain(Size(
        shrinkWrapWidth ? 0.0 : double.infinity,
        shrinkWrapHeight ? 0.0 : double.infinity,
      ));
    }
  }
RenderConstrainedBox
  @override
  void performLayout() {
    final BoxConstraints constraints = this.constraints;
    if (child != null) {
      // 直接把自己的约束传递给子节点,并获取子节点size
      child!.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true);
      // 将子节点的size赋值给自己
      size = child!.size;
      // 自己和子节点的size相同,所以不需要进行align子节点
    } else {
      size = _additionalConstraints.enforce(constraints).constrain(Size.zero);
    }
  }

结论

我们可以得到结论,当直接父节点是CenterAlign等约束Widget,他们的creatRenderObject返回的是RenderPositionedBox, 而RenderPositionedBox计算会先计算子节点大小,然后结算自己的大小,最后根据约束子组件偏移,子组件是可以显示其自身约束,前提是在RenderPositionedBox的松约束下。

而ConstrainedBox、SizeBox等最终的creatRenderObject返回的是RenderConstrainedBox,其直接把父节点约束传递给子节点,当父节点的约束是紧约束,即是expand显示,所有creatRenderObject返回的是RenderConstrainedBox的子节点均会继承约束,即自身约束并不生效。

欢迎各位大佬批评指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值