flutter布局的核心
Constraints go down, Sizes go up, Parent sets position
- 父节点向子节点传约束
 
- 子节点向父节点上传大小
 
- 最后由父节点决定位置
 
不是按照直接约束显示
问题代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | 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, 这个就是重点:
Center的继承关系是:SingleChildRenderObjectWidget>Align>Center。 
ConstrainedBox的继承关系是:SingleChildRenderObjectWidget>ConstrainedBox。 
SizedBox的继承关系是:SingleChildRenderObjectWidget>SizedBox。 
这里先插入一个知识,我们在屏幕上看到的UI都是通过RenderObjectWidget实现的。而RenderObjectWidget中有个creatRenderObject方法生成RenderObject对象,RenderObject实际负责layout()和paint()。
其中Align的creatRenderObject方法返回的是RenderPositionedBox;
SizedBox 和ConstrainedBox的creatRenderObject方法返回的是RenderConstrainedBox;
RenderPositionedBox 和 RenderConstrainedBox
RenderPositionedBox 和 RenderConstrainedBox 最终集成的都是RenderBox。
我们先来对比一下用于计算布局的performLayout方法
RenderPositionedBox
这里在子节点渲染时,修改了布局约束,改为松约束。
其中constraints.loosen()是把min约束给删除,可以理解为置零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | @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);   } }
 
   | 
 
结论
我们可以得到结论,当直接父节点是Center和Align等约束Widget,他们的creatRenderObject返回的是RenderPositionedBox, 而RenderPositionedBox计算会先计算子节点大小,然后结算自己的大小,最后根据约束子组件偏移,子组件是可以显示其自身约束,前提是在RenderPositionedBox的松约束下。
而ConstrainedBox、SizeBox等最终的creatRenderObject返回的是RenderConstrainedBox,其直接把父节点约束传递给子节点,当父节点的约束是紧约束,即是expand显示,所有creatRenderObject返回的是RenderConstrainedBox的子节点均会继承约束,即自身约束并不生效。