美文网首页Flutter
Widget、Element、RenderObject 关系结构

Widget、Element、RenderObject 关系结构

作者: 李小轰 | 来源:发表于2021-09-07 15:36 被阅读0次

序言

使用flutter进行开发也有一段时间了,今天小轰来聊聊widgetelementrenderObject三者间的结构关系。

三棵树

  • 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 内部成员

如上图所示:

  1. 每个widget都提供createElement方法,每个widget最终都会转化成Element
  2. widget被触发build方法的时机特别频繁,canUpdate方法维护Element复用机制。当返回true时,复用旧的Element;
  3. 只有可渲染的widget(子类RenderObjectWidget)提供生成RenderObject的方法

Element

widget的分类相对应,element也区分是否可渲染,继承关系如下:

从上图中我们得知,StatefulElement 在其构造方法中调用了 widget.createState 方法,并赋值 _state 其 widget 对象。
这就是 statefuleWidget createState 方法被调用的时机

重点:Element 内部结构分析
从左往右观察内部成员及方法
整理总结
  1. Element 持有外部 Widget 对象。
  2. Element 提供获取 RenderObject 的方法(get renderObject)。[从自己开始往子节点遍历,直到找出 RenderObjectElement,RenderObjectElement 提供生成RenderObject 的能力]
  3. RenderObjectElement 通过调用 widget.createRenderObject(this)生成RenderObject
  4. 核心方法 mount()`
  1. componentElement的mount方法主要作用是执行build(根据类型区分widget.build, state.build
  2. renderObjectElement 的mount方法主要作用是生成RenderObject
  3. Element创建完成时就会调用 mount, 调用顺序为 mount -> _firstBuild -> reBuild -> performRebuild -> build
  4. Element.markNeedsRebuild 会重新走 reBuild

RenderObject

RenderObject成员结构
成员方法介绍:
  1. parentData: 由父节点赋值,父RenderObj会将子RenderObj的相关数据存储在子元素的parentData中。如在 Stack 布局中,RenderStack就会将子元素的偏移数据存储在子元素的parentData中(具体可以查看Positioned实现)。

  2. layout()方法: 接收两个参数,constrains为父节点对子节点的大小限制;parentUsesSize标识本节点布局发生变化时父节点是否同步发生重布局操作。

  3. _relayoutBoundry: 在layout()方法中进行赋值,当parentUsesSize等于false时,_relayoutBoundry = this(当前RenderObject对象),表示它的大小变化不会影响到parent的大小。否则,_relayoutBoundry = = (parent! as RenderObject)._relayoutBoundary;

  4. markNeedsLayout(): 当一个Element标记为 dirty 时便会重新 build,这时RenderObject便会重新布局,我们是通过调用 markNeedsBuild() 来标记Element为 dirty 的。

从自身开始向parent遍历,直到找到是 relayoutBoundry 的 RenderObject 为止。然后将其标记为 dirty,重新build。

void markNeedsLayout() {
   ...省略
   if (_relayoutBoundary != this) {
     markParentNeedsLayout();
   } else {
     ...省略
   }
 }
  1. performResize(): 在layout方法中,只在sizedByParent 为 true 时,才会被调用。
  2. performLayout(): 在layout方法中被调用,每次layout都会触发

题外话,State中setState做了什么?

State.setState() -> _element.markNeedBuild() -> dirty=true -> readerObject.markNeedLayout()

相关文章

网友评论

    本文标题:Widget、Element、RenderObject 关系结构

    本文链接:https://www.haomeiwen.com/subject/omfpwltx.html