8

Flutter UI 渲染浅析(四)Build

 3 years ago
source link: http://w4lle.com/2020/11/16/flutter-ui-build/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Flutter UI 渲染浅析(四)Build
w4lle

Flutter UI 渲染浅析(四)Build

系列文章的第四篇,本篇文章主要分析下 Element.rebuild() 过程。

源码基于 Flutter v1.20.4。

Flutter UI 渲染浅析(二)VSync 注册 这篇文章中提到,C++ Engine 接收到 VSync 信号后,需要做三件事情:

  • 执行 Dart Framework dart:ui 包下的 _beginFrame()
  • 执行 microtasks 任务
  • 执行 Dart Framework dart:ui 包下的 _drawFrame()

上一篇文章 Flutter UI 渲染浅析(三)Animation 原理 中分析了 _beginFrame() 的过程。

然后接着去处理在 Animate 过程中触发的 microtasks 任务,一般为 Ticker 或者 AnimationController 中 Future 的完成回调。

本篇文章分析下 _drawFrame() 的前半部分—— Element.rebuild() 的过程。

1、_handleDrawFrame()

同上篇文章的逻辑一样,调用到 Dart Framework 的SchedulerBinding._handleDrawFrame() 方法。

// lib/src/scheduler/binding.dart
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
return;
handleDrawFrame();
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
//结束Animate过程记录
Timeline.finishSync(); // end the "Animate" phase
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List<FrameCallback> localPostFrameCallbacks =
List<FrameCallback>.from(_postFrameCallbacks);
//清除列表
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
//SchedulerPhase状态置为idle,等待下一次绘制触发
_schedulerPhase = SchedulerPhase.idle;
//结束Frame过程记录
Timeline.finishSync(); // end the Frame
_currentFrameTimeStamp = null;

主要做了两件事情:

  • 遍历 _persistentCallbacks,由 WidgetsBinding.addPersistentFrameCallback() 注册,从名字也可以看出,它是一个需要持久回调的列表,所以不可删除,每次绘制过程都会回调
  • 遍历_postFrameCallbacks,由 WidgetsBinding.addPostFrameCallback() 注册,只会回调一次,调用过后清除回调列表,一般用于监听绘制完成后处理一些任务

下面主要看下_persistentCallbacks 的执行过程。

1.1、RendererBinding.drawFrame()

Flutter UI 渲染浅析(二)VSync 注册 这篇文章中,我们简单分析了 7 个 Binding 类的作用及其初始化顺序。

RendererBinding 在初始化过程中,注册了_persistentCallbacks 回调,如下。

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
//初始化绘制管线
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
//注册window回调
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
//初始化RenderObject根节点RenderView
initRenderView();
_handleSemanticsEnabledChanged();
//这里添加_persistentCallbacks回调
addPersistentFrameCallback(_handlePersistentFrameCallback);
//_persistentCallbacks回调
void _handlePersistentFrameCallback(Duration timeStamp) {
//真正执行frame绘制
drawFrame();
_mouseTracker.schedulePostFrameCheck();

_handlePersistentFrameCallback(Duration timeStamp) 方法是 _persistentCallbacks 回调列表的一个子元素,其中去调用 drawFrame() 方法。

由于 WidgetsFlutterBinding 的混入顺序

// lib/src/widgets/binding.dart
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;

WidgetsBindingRendererBinding 之后,所以会先执行 WidgetsBinding.drawFrame() 方法。

1.2、WidgetsBinding.drawFrame()

WidgetsBinding.drawFrame() 实现:

// lib/src/widgets/binding.dart WidgetsBinding
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
//构造BuildeOwner,主要负责Widget的build过程
_buildOwner = BuildOwner();
buildOwner.onBuildScheduled = _handleBuildScheduled;
//注册window相关回调
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
//导航channel
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
@override
void drawFrame() {
//renderViewElement是根RenderObject RenderView对应的Element
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
//调用mixin的drawFrame方法,即RendererBinding.drawFrame()
super.drawFrame();
buildOwner.finalizeTree();
  • buildOwner.buildScope() 触发Widget Tree、Element Tree、RenderObject Tree三棵树的构建或刷新过程
  • super.drawFrame() 调用父类的 drawFrame() 方法,由于 WidgetsBinding 混入了 RendererBinding ,所以这里会去调用 RendererBinding.drawFrame() ,下篇文章会继续分析
  • buildOwner.finalizeTree() 卸载未激活状态的 Element 节点。未激活状态的节点在一个绘制帧周期内,是有可能被重新激活的,如果没有重新激活,那么就卸载掉

1.3、BuildOwner.buildScope()

// lib/src/widgets/framework.dart BuildOwner
void buildScope(Element context, [ VoidCallback callback ]) {
// 记录 Build 过程
Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent);
_scheduledFlushDirtyElements = true;
if (callback != null) {
_dirtyElementsNeedsResorting = false;
// 执行回调,在App启动构建三棵树时会用到
callback();
// 重排序,高度优先
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
// 触发 Elemeng.rebuild() 更新三棵树
_dirtyElements[index].rebuild();
index += 1;
// 在等待VSync 信号回调过程中,有可能又有新的标脏节点进来
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
// 重排序,高度优先
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
index -= 1;
} finally {
for (final Element element in _dirtyElements) {
// 清除 Element 脏标记
element._inDirtyList = false;
// 清空脏列表
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
// 结束 Build 记录过程
Timeline.finishSync();
  • 执行回调,在 App 启动时WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) 会用到,用于构建出三棵树,callback 为 element.mount(null, null);
  • _dirtyElements 脏列表重排序,在等待 VSync 信号回调过程中,有可能又有新的标脏节点进来
  • 脏列表中的节点,即调用了 State.setState() 的节点,触发 Element.rebuild() 更新三棵树,这里的重点也是Element.rebuild()。
  • 清除 Element 脏标记,清空脏列表

1.4、Element.rebuild()

// flutter/lib/src/widgets/framework.dart Elememt
void rebuild() {
if (!_active || !_dirty)
return;
performRebuild();
@protected
void performRebuild();

逻辑比较简单,调用 performRebuild(),它是一个空方法,实现在子类。

2、Widget、Element 与 RenderObject

在继续分析后续流程之前,先简单梳理下 Widget、Element 与 RenderObject 之间的关系,以及三棵树与 Layer Tree 之间的关系。

Flutter 开发者最熟悉的就是 Widget 了。

Widget 是面向开发者的接口,它是对UI的描述性表达,即是用于描述 Element 的配置的。

Widget 是声明式的 UI 结构,开发者通过组合 Widget 构建出想要的UI效果。

Widget 是不可变的(immutable),这就意味着每次刷新,都会重新构建出新的Widget对象,创建的开销很小,成本较低。

我们通常将 Widget 组合构建出的 UI 层级结构称为 Widget Tree,但相比 Element Tree,实际上并不存在 Widget Tree,由于 Widget 节点挂载在 Element 节点上,所以我们可以抽象为 Widget Tree。

Widget 提供 createElement()createRenderObject() (并不是所有)用于构建 Element 和 RenderObject。

Widget 主要有三种类型:

  • ProxyWidget 代理类,不直接参与构建UI,它们可以为其他 Widget 提供一些附加信息。例如 InheritedWidget 可以在其子树中传递附加信息;ParentDataWidget 用于提供其子树的布局信息
  • ComponentWidget 组合类,不直接参与绘制,它们用来组合包装用来构建复杂的UI布局。一般都是 StatefullWidget 或者 StatelessWidget 的子类,例如 RaisedButtonScaffoldTextGestureDetectorContainer
  • RenderObjectWidget 绘制类,可以构建出 RenderObject 用来布局和绘制

Element 是响应式编程的基础,频繁的创建 Element 会对性能有影响,所以只有在必要条件下才会创建一个新的 Element 对象,大部分情况下会进行复用,主要包含两个职责:

  • 持有 Widget 和 RenderObject 的引用,协调二者之间的数据绑定关系
  • 根据 Widget 的变化来创建或更新 Element Tree,包括挂载、更新、更改位置、卸载等

Element 和 Widget 是一一对应的关系,同样类型的 Widget 构建出同样类型的 Element。

RenderObject 用来布局和绘制,处理输入事件等。

Element 和 RenderObject 不是一一对应的,只有可以绘制的节点才有 RenderObject 对象。

Render Tree 用来布局和绘制 RenderObject 节点,最终生成 Layer Tree 提交给 C++ Engine。它的根节点是 RenderView。

他们三者之间的关系:

Element 持有 Widget 引用和 RenderObject 应用(可能没有),Widget 用来构建 RenderObject 对象。

对于 StatefullElement 来说,它还会持有 State 的引用。

这里需要注意,Element 的 child 是 Widget 中 build() 方法构建出来的 Widget 所对应的 Element,下面会用到。

Widget、Element、RenderObject 构建出三棵树 Widget Tree、Element Tree、Render Tree,它们共同组成了 Flutter 对于UI的组织描述。

前两棵树可以认为是面向开发者的,它们构成了声明式UI、响应式UI的基础,Render Tree 用来真正的布局和绘制,最后生成 Layer Tree,并保存在 Scene 对象中,提交给 C++ Engine 做光栅化合成。

那么,可不可以绕开 Widget、Element、RenderObject 来进行绘制,其实是可以的,它们只是用来组织描述绘制信息的,我们可以直接拿到 Canvas 进行绘制,只要最终可以生成Layer Tree 保存在 Scene 中就可以。

例如 这个例子 🌰

更进一步的,甚至可以绕过或者舍弃 Dart Framework,直接对接 C++ Engine,任何可以组织描述UI绘制结构的组织形式,理论上都可以桥接到 C++ Engine。

例如基于 W3C 标准的 CSS + JS/TS 组织的UI描述,通过绑定JS与C++ Engine,将绘制信息发送给 Engine,理论上也是可行的,如下图

3、Element.performRebuild()

继续上面分析到 Element.performRebuild()

分 Element 类型看下实现

ComponentElement:

// lib/src/widgets/framework.dart ComponentElement
@override
void performRebuild() {
Widget built;
built = build();
} catch (e, stack) {
_debugDoingBuild = false;
// 错误情况,这里是构建红屏的地方
built = ErrorWidget.builder();
} finally {
// 改变标志位
_dirty = false;
_child = updateChild(_child, built, slot);
} catch (e, stack) {
// 错误情况,这里也是构建红屏的地方
built = ErrorWidget.builder();
_child = updateChild(null, built, slot);
// lib/src/widgets/framework.dart StatefullElement
@override
void performRebuild() {
if (_didChangeDependencies) {
// 依赖的祖先节点如果有变化,需要调用
_state.didChangeDependencies();
_didChangeDependencies = false;
super.performRebuild();

主要做了两件事情:

  • build() 构建子 Widget,注意这里是 子Widget
  • _updateChild() 创建或更新子Element,注意这里是 子Element

为什么强调子Widget和子Element,因为在这个Element对象中,它对应的 Widget 和 Element 就是Element自己合它持有的 Widget。这里很容易搞混。

RenderObjectElement:

// lib/src/widgets/framework.dart RenderObjectElement
@override
void performRebuild() {
widget.updateRenderObject(this, renderObject);
// lib/src/widgets/basic Stack
class Stack extends MultiChildRenderObjectWidget {
@override
void updateRenderObject(BuildContext context, RenderStack renderObject) {
assert(_debugCheckHasDirectionality(context));
renderObject
..alignment = alignment
..textDirection = textDirection ?? Directionality.of(context)
..fit = fit
..clipBehavior = overflow == Overflow.visible ? Clip.none : clipBehavior;

widget.updateRenderObject() 的作用是把 Widget 中的属性值,绑定到 RenderObject 中,属性的类型一一对应。

3.1、Element.build()

build() 在各个类型的 Element 的实现:

// StatefullElement
@override
Widget build() => _state.build(this);
// StatelessElement
@override
Widget build() => widget.build(this);
// ProxyElement
@override
Widget build() => widget.child;

3.2、Element.updateChild()

这个方法是响应式UI的基础,也是 Dart Framework 的核心方法之一,看下实现:

@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
// case 1
if (child != null)
deactivateChild(child);
return null;
Element newChild;
if (child != null) {
// case 2
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
// case 2.1
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
// case 2.2
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
newChild = child;
} else {
// case 2.3
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot);
} else {
// case 3
newChild = inflateWidget(newWidget, newSlot);
return newChild;

首先要先明确,这个方法是用来更新子树的,第一个参数 child子Element,第二个参数newWidget子Widget

分几种情况:

  • case 1:newWidget 为空,也就是当前 Element 节点对应的 Widget build() 返回了空,那么标记child为非激活状态(当前帧绘制完成后会被卸载),然后返回空
  • case 2:如果child不为空,也就是之前构建过一次子Element
    • case 2.1:如果子Element对应的widget 即 child.widget 和新构建的 newWidget 相等,直接更新子widget,如果插槽不同,更新下插槽
    • case 2.2:如果 Widget.canUpdate(child.widget, newWidget) ,判断标准是 runtimeTypekey 都相等,那么调用 update() 更新 child
    • case 2.3:否则child不可复用,标记child为非激活状态(当前帧绘制完成后会被卸载),然后构建出一个新的 Element 节点,挂载到Element Tree上
  • case 3:否则 child 为空,不可复用,构建出一个新的 Element 节点,挂载到Element Tree上

下面看下几个关键的方法

3.3、Element.update()

Element 类实现:

// lib/src/widgets/framework.dart Element
@mustCallSuper // 子类复写该方法必须调用super
void update(covariant Widget newWidget) {
_widget = newWidget;

直接更新 Element 子节点的 _widget 引用。

子类复写该方法必须调用super。

看下子类的实现。

3.3.1、RenderObjectElement.update()

abstract class RenderObjectElement extends Element {
@override
void update(covariant RenderObjectWidget newWidget) {
super.update(newWidget);
widget.updateRenderObject(this, renderObject);
_dirty = false;

同上面一样,把 newWidget 中的属性值,绑定到 RenderObject 中。

SingleChildRenderObjectElementMultiChildRenderObjectElementRenderObjectElement 的子类,分别看下实现。

class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void update(SingleChildRenderObjectWidget newWidget) {
super.update(newWidget);
// 更新子树
_child = updateChild(_child, widget.child, null);
  • 调用super,复用 RenderObjectElement.update() 逻辑
class MultiChildRenderObjectElement extends RenderObjectElement {
@override
void update(MultiChildRenderObjectWidget newWidget) {
super.update(newWidget);
_children = updateChildren(_children, widget.children, forgottenChildren: _forgottenChildren);
_forgottenChildren.clear();
  • 调用super,复用 RenderObjectElement.update() 逻辑
  • 通过差分算法将新构造的 widget.children 绑定到已有的 _children 上来更新子树,updateChildren() 逻辑虽然看起来很多,但是还比较好理解,这里就不放源码了,说下逻辑
    • 首先从 topIndex 到 bottomIndex 遍历 oldChildElement 和 newChildWidget,如果Widget.canUpdate(oldChild.widget, newWidget),那么updateChild()更新子树 updateChild(),直到匹配失败,记录 topIndex 累加值
    • 从 bottomIndex 到 topIndex 遍历oldChildElement 和 newChildWidget,直到匹配失败,记录 bottomIndex 累减值,这里不更新子树
    • 遍历缩小了的 oldChildElement 列表,记录 oldChild.widget.key 和 oldChild 到 map,key为空的反激活
    • 遍历缩小了的 newChildWidget 列表,匹配 map 中的key类型,updateChild() 更新子树,未匹配到的反激活
    • 最后更新第二步得到剩余的部分

3.3.2、StatefullElement.update()

@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
final StatefulWidget oldWidget = _state._widget;
// Notice that we mark ourselves as dirty before calling didUpdateWidget to
// let authors call setState from within didUpdateWidget without triggering
// asserts.
_dirty = true;
_state._widget = widget as StatefulWidget;
final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
// 更新子树
rebuild();
  • 调用 super
  • 更新 state 中 widget 的引用
  • 调用 _state.didUpdateWidget(oldWidget)
  • 调用 rebuild() 更新子树,由于 rebuild() 一定会触发 build() 方法调用,所以这里进行标脏

StatelessElement.update() 类似,不写出来了。

3.4、Element.inflateWidget()

@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
final Key key = newWidget.key;
if (key is GlobalKey) {
// 如果key类型是 GlobalKey,从非激活状态的列表中尝试匹配类型相同的节点,抢救复用一下
final Element newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
newChild._activateWithParent(this, newSlot);
final Element updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild;
// 通过 Widget,创建对应的 Element
final Element newChild = newWidget.createElement();
// 挂载到 Element Tree 上
newChild.mount(this, newSlot);
return newChild;
  • 如果key类型是 GlobalKey,从非激活状态的列表中尝试匹配类型相同的节点,抢救性复用一下
  • 通过 Widget,创建对应的 Element
  • 挂载到 Element Tree 上

3.5、Element.mount()

挂载到Element Tree上

@mustCallSuper
void mount(Element parent, dynamic newSlot) {
// 更新父节点信息
_parent = parent;
// 更新插槽信息
_slot = newSlot;
// 更新深度
_depth = _parent != null ? _parent.depth + 1 : 1;
// 从初始化状态更改为激活状态
_active = true;
if (parent != null)
// 绑定 BuildOwner 对象
_owner = parent.owner;
final Key key = widget.key;
if (key is GlobalKey) {
// 如果是 GlobalKey,注册到公共map,全局复用
key._register(this);
// 从 parent 更新 _inheritedWidgets,用于传递附加信息
_updateInheritance();
  • 更新父节点信息
  • 更新插槽信息
  • 从初始化状态更改为激活状态
  • 绑定 BuildOwner 对象
  • 如果是 GlobalKey,注册到公共map,全局复用
  • 从 parent 更新 _inheritedWidgets,用于传递附加信息

如果子类复写该方法,那么必须要调用 super。

3.5.1、ComponentElement.mount()

abstract class ComponentElement extends Element {
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_firstBuild();
void _firstBuild() {
rebuild();
  • 调用 super() 调用 Element.mount()
  • 调用 _firstBuild() -> rebuild() -> performRebuild() 构建 Element 子树

下面还有 SingleChildRenderObjectElement 、MultiChildRenderObjectElement 等子类,逻辑跟update()差不多,就不列出来了。

3.5.2、RenderObjectElement.mount()

abstract class RenderObjectElement extends Element {
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
// 构建 RenderObject
_renderObject = widget.createRenderObject(this);
// 将 RenderObject 挂载到 Render Tree 上
attachRenderObject(newSlot);
_dirty = false;
  • 调用 super() 调用 Element.mount()
  • 构建 RenderObject
  • 将 RenderObject 挂载到 Render Tree 上

3.6、RenderObjectElement.attachRenderObject()

abstract class RenderObjectElement extends Element {
@override
void attachRenderObject(dynamic newSlot) {
_slot = newSlot;
// Element Tree 向上遍历祖先节点,找到第一个 RenderObject 节点
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
// 根据规则插入到 Render Tree 中,需要子类实现
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
// Element Tree 向上遍历祖先节点,找到第一个 ParentDataElement 节点
final ParentDataElement<ParentData> parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
void _updateParentData(ParentDataWidget<ParentData> parentDataWidget) {
parentDataWidget.applyParentData(renderObject);
  • Element Tree 向上遍历祖先节点,找到第一个 RenderObject 节点
  • 根据规则插入到 Render Tree 中,需要子类实现
  • Element Tree 向上遍历祖先节点,找到第一个 ParentDataElement 节点,ParentDataElement 节点中记录着布局位置信息,如果没有找到返回空
  • 根据 ParentDataElement 找到对应的 ParentDataWidget,调用 applyParentData()

其中,ParentDataElement 根据参数绑定了 ParentDataWidget 类型,并通过泛型绑定了 ParentData 类型。

3.6.1、ParentDataWidget.applyParentData()

ParentDataWidget 是 ProxyWidget 的子类,它的子类包括 Flexible、LayoutId、Positioned、KeepAlive 等Widget。

ParentDataWidget 使用泛型绑定了 ParentData 类型。

以 Flexible 为例看下实现:

class Flexible extends ParentDataWidget<FlexParentData> {
@override
void applyParentData(RenderObject renderObject) {
final FlexParentData parentData = renderObject.parentData as FlexParentData;
bool needsLayout = false;
if (parentData.flex != flex) {
parentData.flex = flex;
needsLayout = true;
if (parentData.fit != fit) {
parentData.fit = fit;
needsLayout = true;
if (needsLayout) {
final AbstractNode targetParent = renderObject.parent;
if (targetParent is RenderObject)
targetParent.markNeedsLayout();
  • 更新布局属性信息
  • RenderObject 节点 Layout 标脏,记录在 BuildOwner._nodesNeedingLayout 列表中,等待下一步 Layout 处理

本篇文章介绍了 WidgetsBinding.drawFrame() 的过程,以及 Widget、Element、RenderObject 及三棵树的关系,梳理了build() 过程在三棵树之间的流转关系,通过 Element Tree 和 Widget Tree 构建了 Render Tree,最终触发 RenderObject.markNeedsLayout() Layout 标脏操作,记录在 BuildOwner._nodesNeedingLayout 列表中,等待下一步 Layout 处理。

下一篇文章将继续分析 RendererBinding.drawFrame() 中 Layout 过程。

本文链接: http://w4lle.com/2020/11/16/flutter-ui-build/

版权声明:本文为 w4lle 原创文章,可以随意转载,但必须在明确位置注明出处!
本文链接: http://w4lle.com/2020/11/16/flutter-ui-build/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK