Flutter里的三棵树
首先,flutter采用数据驱动UI的设计,当数据变化时重新构建UI。基于这个设计,UI的载体widget设计必然不能像Android的View一样。Android的view承载了数据、重用、位置、渲染所有功能,因此创建一个view的成本很高。flutter的widget设计简单的多,widget树作为第一棵树,只是作为数据的载体,用于描述UI,所以构建成本非常低。位置计算和渲染拆分出来由第三棵树render树处理,第二棵树element树作为widget和render的中间体,主要负责组件重用和更新。
widget树
Widget 是 flutter 体系中最基础的组成部分
1 | /// Describes the configuration for an [Element]. |
从 sdk 注释可以看出 widget 是对 element的描述,并且是不可变的。
widget 主要分为 StatefulWidget, StatelessWidget, InheritedWidget 和 RenderObjectWidget。最常见的是 StatefulWidget 和 StatelessWidget。如果组件本身不包含状态,创建完不会发生变化,那么就可以用 StatelessWidget,例如 TextField。
StatefulWidget
TextField -> StatefulWidget
InheritedWidget
MediaQuery -> InheritedWidget
StatelessWidget
Text -> StatelessWidget
RenderObjectWidget
Row -> Flex -> MultiChildRenderObjectWidget -> RenderObjectWidget
SizedBox -> SingleChildRenderObjectWidget -> RenderObjectWidget
element树
widget 和element 是1对1的关系,每个widget 都会有一个对应的element。看Widget 类实现就知道,Widget 类有个createElement()
的抽象方法,用于创建element。这个方法会在updateChild()
方法中调用。具体分析可以看element 重用。
element 可以分为两种ComponentElement 和RenderObjectElement。故名思义RenderObjectElement适用于绘制的element,而ComponentElement 是用于组合不同的element。
由于widget 和element 是1对1对于的,所以上面分类的4种widget 可以和element 的对应关系如下:
StatefulWidget -> StatelessElement -> ComponentElement
InheritedWidget -> InheritedElement -> ProxyElement -> ComponentElement
StatelessWidget -> StatefulElement -> ComponentElement
RenderObjectWidget -> RenderObjectElement
element 树由Element 类中的_parent
对象和visitChildren()
方法构成。通过这两者可以实现element 树自上向下或自下而上的遍历。
render树
RenderObject 和RenderObjectElement 一一对应,所以相当于从element 树中提取RenderObjectElement 的部分组成的树(当然很多ComponentElement本身也是由RenderObjectElement 组成的,这部分RenderObjectElement 也会在render树中)。
RenderObject 负责页面最终的布局和绘制,其实RenderObject 和 Android View 比较像,最重要的方法是layout()
和paint()
负责控制组件的布局及绘制。