序言
使用flutter进行开发也有一段时间了,今天小轰来聊聊widget、element、renderObject三者间的结构关系。
三棵树
- Widget 树负责配置信息,我们平时写代码写的就是这棵树。
- RenderObject 树是渲染树,负责计算布局,绘制,Flutter 引擎就是根据这棵树来进行渲染的。
-
Element 树作为中间者,管理着将 Widget 生成 RenderObject和一些更新操作。
从上图可以看出,widget 树和 Element 树节点是一一对应关系,每一个 Widget 都会有其对应的 Element。但是 RenderObject 树则不然,只有需要渲染的 Widget 才会有对应的节点。
Widget
我们平时用 Widget 使用声明式的形式写出来的界面,可以理解为 Widget 树,这是要介绍的第一棵树。下面,我们来看看 Widget 的结构设计:
-
widget 分为两种类型
widget 从渲染的角度进行分类,分为 可渲染Widget 与 不可渲染Widget。像我们常用的 statelessWidget 与 statefulWidget 就属于不可渲染的Widget。
Widget 分类
-
widget 内部结构
widget 内部成员
如上图所示:
- 每个widget都提供
createElement方法,每个widget最终都会转化成Element; - widget被触发
build方法的时机特别频繁,canUpdate方法维护Element复用机制。当返回true时,复用旧的Element; - 只有可渲染的widget(子类
RenderObjectWidget)提供生成RenderObject的方法
Element
与widget的分类相对应,element也区分是否可渲染,继承关系如下:
从上图中我们得知,StatefulElement 在其构造方法中调用了
widget.createState方法,并赋值 _state 其 widget 对象。
这就是 statefuleWidget createState 方法被调用的时机。
重点:Element 内部结构分析
从左往右观察内部成员及方法
整理总结
- Element 持有外部 Widget 对象。
- Element 提供获取 RenderObject 的方法
(get renderObject)。[从自己开始往子节点遍历,直到找出RenderObjectElement,RenderObjectElement 提供生成RenderObject 的能力] - RenderObjectElement 通过调用
widget.createRenderObject(this)生成RenderObject - 核心方法 mount()`
componentElement的mount方法主要作用是执行build(根据类型区分widget.build,state.build)renderObjectElement的mount方法主要作用是生成RenderObject- Element创建完成时就会调用
mount, 调用顺序为 mount -> _firstBuild -> reBuild -> performRebuild -> build- Element.markNeedsRebuild 会重新走 reBuild
RenderObject
RenderObject成员结构
成员方法介绍:
-
parentData: 由父节点赋值,父RenderObj会将子RenderObj的相关数据存储在子元素的parentData中。如在 Stack 布局中,RenderStack就会将子元素的偏移数据存储在子元素的parentData中(具体可以查看Positioned实现)。 -
layout()方法: 接收两个参数,constrains为父节点对子节点的大小限制;parentUsesSize标识本节点布局发生变化时父节点是否同步发生重布局操作。 -
_relayoutBoundry: 在layout()方法中进行赋值,当parentUsesSize等于false时,_relayoutBoundry = this(当前RenderObject对象),表示它的大小变化不会影响到parent的大小。否则,_relayoutBoundry = = (parent! as RenderObject)._relayoutBoundary; -
markNeedsLayout(): 当一个Element标记为 dirty 时便会重新 build,这时RenderObject便会重新布局,我们是通过调用 markNeedsBuild() 来标记Element为 dirty 的。
从自身开始向parent遍历,直到找到是 relayoutBoundry 的 RenderObject 为止。然后将其标记为 dirty,重新build。
void markNeedsLayout() { ...省略 if (_relayoutBoundary != this) { markParentNeedsLayout(); } else { ...省略 } }
-
performResize(): 在layout方法中,只在sizedByParent为 true 时,才会被调用。 -
performLayout(): 在layout方法中被调用,每次layout都会触发
题外话,State中setState做了什么?
State.setState() -> _element.markNeedBuild() -> dirty=true -> readerObject.markNeedLayout()











网友评论