通过 Flutter 学习一下 UI 技术架构

不小心被推到前端话题了我,下面只是一些临时的记录而已,不推荐阅读,打扰了

还很乱套,做个记录先。。。

Flutter 的三层结构

  1. widget 层:用来定义state、props 和组件之间的关系,以及 widget 上下级关系
  2. element 层:更像是 widget 层的 shadow layer,element 实际上负责构成了 flutter 内部的 UI 树
  3. render object 层:用于封装 UI 底层的渲染逻辑

常见的 widget 和 element 基本上是一对一的关系,如下图所示:

《通过 Flutter 学习一下 UI 技术架构》
《通过 Flutter 学习一下 UI 技术架构》

图中 w1/w2 表示 widget,其中 w1 是 w2 的父节点。E1 和 E2 表示 Element。上下级的 Widget 之间具有 build 关系,w2 是通过调用 w1 的 build 方法创建的。

实际在界面渲染过程中,flutter 并没有直接使用 Widget 这个数据结构来表示组件树,而是额外用 Element 类来表示。如上图,每个 Element 实例是通过调用 Widget 的 createElement 方法创建的,创建的 Element 保留了 Widget 实例的引用。

在渲染整个 UI 时,widget 和 element 之间的调用关系是:

  1. 系统先创建最上层的 w1
  2. 调用 w1.createElement 方法创建同级关系的 e1
  3. e1 再调用 w1 的 build 方法,创建 w1 的子节点 w2
  4. 然后在 w2 上重复步骤 1 ~ 3,逐级的将一层层的 widget 和 element 创建出来,形成整个 UI 结构

可以看到的是 Widget 上下级之间是没有直接的引用关系的,而 Element 上下级之间是有直接引用关系的,最后的 UI 树效果如下图所示:

《通过 Flutter 学习一下 UI 技术架构》
《通过 Flutter 学习一下 UI 技术架构》

其中,图中蓝色的节点表示 Element 实例,绿色节点表示 Widget 实例。

上面介绍的 Widget 和 Element 其实是主要是封装一些 “容器” 类组件,像 Text 等“叶子”节点类型的组件,则没有 _child 而是有 renderObject 属性。就以 Text 为例。创建 Text 节点的时候其实会创建以下几个对象:

  1. Widget: Text 实例
  2. Widget: RichText 实例,作为 Text 示例的子节点
  3. Element: LeafRenderObjectElement 实例
  4. Render Object: RenderParagraph

渲染流程也和上面介绍的流程不一样:

  1. Text 创建 ComponentElement 实例
  2. ComponentElement 实例反过来调用 Text build 方法,创建 RichText 实例,然后 ComponentElement 实例 updateChild 方法去更新 RichText 实例,这个过程会调用到 RichText 实例的 update 方法
  3. RichText 实例的 update 方法当中会调用 RichText 实例的 updateRenderObject 方法
  4. 调用 updateRenderObject 方法,而不是更新 child 节点,因为是叶子节点嘛,本来就没有子节点了
  5. updateRenderObject 方法当中会更新 RenderParagraph 的属性,比如 textAlign、text 等
  6. RenderParagraph 实例的属性更新时,会将自身登记到渲染模块的 dirty nodes 当中去
  7. 等 UI 树更新完以后,渲染模块会遍历 dirty nodes,进行最后的 UI 渲染环节

结合上面的内容,UI 树的结构直观上应该是下图这个样子:

《通过 Flutter 学习一下 UI 技术架构》
《通过 Flutter 学习一下 UI 技术架构》

其中红色的对象表示的是各种 RenderObject 子类实例

总结:

  1. 大部分的 Widget 和 Element 是一对一关系
  2. Element 实例组成了 UI 树
  3. 叶子节点的 Element 实例会包含 RenderObject 实例
  4. RenderObject 的各种子类封装了最终的绘制逻辑,底层用的是 canvas 接口来绘制
  5. 更新 UI 树的过程中:
    1. widget 用来构建新的 Element 树
    2. 叶子节点类型 Element 实例会包含 RenderObject 实例
    3. 叶子节点属性更新时,实际更新的是 RenderObject 中的实例
  6. 当 Element UI 树更新完成之后,渲染模块会遍历 dirty nodes 来重新布局和渲染

问题

  1. Text 节点是如何渲染的呢
    1. 首先调用 pipeline owner 的 flushLayout
    2. 然后调用它的 flushPaint 进行渲染
      1. pipeline owner 会将 dirty nodes 根据深度来进行排序,层次浅的先绘制
      2. 绘制过程当中会调用 RenderObject 的 paint 方法
      3. paint 方法当中封装的绘制 canvas 的逻辑
      4. RenderParagraph 方法当中封装的是将文本绘制到 canvas 上面的逻辑,主要是用了一个叫做 TextPainter 的模块
    原文作者:杨辰
    原文地址: https://zhuanlan.zhihu.com/p/40919250
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞