Flutter 的 RenderBox 模型小记

79 阅读2分钟

原文来自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 的副轴长度取决于其中一个子控件。 这个场景还是非常有用的,经常能碰到,好评。

参考

Boxy in Pub

Boxy doc