原文来自boxy库的文档,写的很好。
本文基本是对前几章翻译的总结。
原则
约束向下传递,尺寸向上传递。
盒约束
BoxConstrains 定义了每个轴(方向)上的最值长度。共有四种类型,默认为无约束。
- tight,最大最小值相同,子控件无法在该轴上控制自己的大小。如SizedBox。
- loose,最小值为0且最大值不为0,子控件在这个范围内控制自己的大小。如Center。
- unbounded,最大值为无限。子控件必须自己决定大小,如果试图占满所有可用空间,则会报错。
- unconstrainted,最小值为0且最大值为无限。子控件大小完全由自己决定。如Column的子控件的高度。
因此无边界中不能再有无边界的子控件,约束冲突导致无法计算出Size。
另外,紧约束的传递是强制的,子控件即使为无约束,也不能突破父控件的紧约束。 例如,下列代码中Text的宽度会是100。
SizedBox(
width: 100,
child: SizedBox(
width: 200,
child: Text('Is my width 100 or 200?'),
),
)
三棵树
分别是 Widgets Tree,Elements Tree 和 Render Tree。
State 是个带有可重写方法的永久实例,一般被用以hook控件的初始化,构建和销毁时机。 实际上它只是 ComponentElement 的一个花哨的委托,用来隐藏丑陋的内部结构。 Elements会实现 BuildContext,实际上也是为了隐藏ugly internals。
通过递归调用Widgets的createElement(),Flutter替我们构建了Element Tree,处理了递归挂载,构建,重绘和卸载等一系列脏活。
当 RenderObjectElement 被挂载时,其会调用 creatRenderObject() 来创建一个 RenderObject。这才是最终真正被渲染在屏幕上的东西。
严格的说Widgets并非一个树(没有共享同一个根节点),它更像是用户定义的数据结构,作为UI的配置。
Boxy 解决了啥问题
这是额外的一部分,简单写一下 Boxy 这个库。
本质上它是一个 CustomPaint 的更强大的版本,允许你手动控制一些本来无法控制的属性,或者让尺寸取决于子控件的尺寸。
如果碰到实在无法实现的UI,可以看看它。
例如 Dominant,可以让一个 BoxyRow 或 BoxyColumn 的副轴长度取决于其中一个子控件。 这个场景还是非常有用的,经常能碰到,好评。