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;
WidgetsBinding
在 RendererBinding
之后,所以会先执行 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
的子类,例如 RaisedButton
、Scaffold
、Text
、 GestureDetector
、 Container
等
- 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)
,判断标准是 runtimeType
和key
都相等,那么调用 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 中。
SingleChildRenderObjectElement
、MultiChildRenderObjectElement
是 RenderObjectElement
的子类,分别看下实现。
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/