这一篇我们讲解 flutter 中三大类:Widget 、Element 、RenderObject 的关系,及在整个应用中的处理时机。
基础讲解 这里先讲解基本的概念,再结合代码运行时来说明创建流程 及相互关系 。
Widget 相关的继承关系 图: 由上图可知,Widget 类是其他类的基类。其下主要有 4 类:
StatelessWidget: 无状态组件。需要实现 build 方法。不多介绍。
StatefulWidget: 有状态组件。需要实现 createState 方法。不多介绍。
ProxyWidget: 代理组件。其下主要是:
InheritWidget: 继承组件。主要用于在组件树上寻找对应的基类组件,通常用于共享数据。比如 Theme、DefaultTextStyle 等 Widget,可以在任意层组件向上寻找 最近的 InheritWidget
基类。
ParentDataWidget: 用于自定义关于父组件存储数据到子组件里的逻辑。比如:Stack
组件的 children 中可以使用 Positioned
组件。其中 Positioned
组件就是 ParentDataWidget
组件,它实现了 applyParentData
方法,用于将该 Widget 的 left、right、top、bottom 值写入该 Widget 的 child 对应的 RenderObject 的 parentData 属性中(这句话有点长,你品,你细品)
RenderObjectWidget: 渲染对象组件。虽然该 RenderObjectWidget 按照继承关系来说,是和上面三类 Widget 同一层级,但用途却不一样。上面三类,前两种都可以理解为很多 Widget 的组合,它不能创建新的原生组件,只能使用现有的组件;ProxyWidget 自身没有类似 build 的方法,只保存了一个 child 属性,当然你还能定义各种数据,这才是它的主要作用:作为代理,可以存储数据给所有后代用。并且当数据改变时,可以通过某种方式通知所有使用过它数据的后台去更新视图 (针对 InheritWidget
说的)。 该 RenderObjectWidget
组件的作用不一样:它自己创建对应的 RenderObject
用于控制渲染!大家可能觉得这个会用的很少,比如大部分的 UI 组件官方都提供了,我们用它干啥呢?是不是对我们没用了?大错特错啦 ,所有的 UI 组件的基类都是 RenderObjectWidget
!通过该类,我们才能自定义新的 UI 组件(比如官方组件不能满足你的需求,是不是只能自己写一个组件了?虽然通常能使用 StatelessWidget 完成对应的需求,但设计到布局、绘制的,就需要继承该类来自己撸了)。根据子组件的个数,其下分为这么三类:
SingleChildRenderObjectWidget: 单儿砸组件。比如:Padding、DecoratedBox、ConstrainedBox 等( 有同学也许会说:Container? 不对,Container 的基类是 StatelessWidget,它只是组合了很多其他的组件,比如刚刚才说的几个)。
MultiChildRenderObjectWidget: 多 child 组件。比如:Stack
、Flex
等。通常关注的是布局。
LeafRenderObjectWidget: 没儿砸组件,作为叶节点。无布局方法。比如:_SliderRenderObjectWidget(是 Slider
组件的底层 RenderObjectWidget,该 Widget 对应的 RenderObject _RenderSlider
主要关注绘制,即 paint
方法,对 layout 相关不感冒)
下面是 Widget
基类的抽象定义:
其实关注点主要就一个,需要实现一个 createElement 方法。同时也保存了一个 key
属性,用于替换、复用对应的 Element
。对于四个子类的细节这里就不细说了(大致可参考上面说的)。关于这四类 Widget
,后面单独整理几篇文章来说明(TODO ) 。下面看一下跟这个 Widget
相关的 Element
是什么。
Element 同样,首先看一下 Element
的继承关系:
Element 下就只有两类:
ComponentElement: 组件元素。这一类 Element
主要是组合其他 Element,或者是提供代理功能,分别与 StatelessWidget/StatefulWidget、ProxyWidget 对应。
RenderObjectElement: 渲染对象元素。这与 RenderObjectWidget
对应。其下的四个子类,除了 RootRenderObjectElement
外,其余三个,分别与 LeafRenderObjectWidget
、SingleChildRenderObjectWidget
、MultiChildRenderObjectWidget
对应。
现在我们需要先理清 Widget
与 Element
的关系,可以通过查看 Widget
和 Element
的相关注释发现:
/// Describes the configuration for an [Element]. /// /// Widgets are the central class hierarchy in the Flutter framework. A widget /// is an immutable description of part of a user interface. Widgets can be /// inflated into elements, which manage the underlying render tree.
大致意思是:Widget
是 Element
的配置。Widget
在 flutter 中处于中心位置,一个 Widget 是 UI 的不可变描述(如果可变,则使用 StatefulWidget) ,会被碾平为 Element
。同时,Element
也管理着底层的渲染树(由 RenderObject 构成的渲染树)。
很好理解了,Widget
只是配置,它只是用来形成 Element
的配置。
根据定义,我们首先简单看一下 BuildContext
定义了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 abstract class BuildContext { Widget get widget; BuildOwner get owner; RenderObject findRenderObject(); Size get size; InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }); InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }); InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType); Widget ancestorWidgetOfExactType(Type targetType); State ancestorStateOfType(TypeMatcher matcher); State rootAncestorStateOfType(TypeMatcher matcher); RenderObject ancestorRenderObjectOfType(TypeMatcher matcher); void visitAncestorElements(bool visitor(Element element)); void visitChildElements(ElementVisitor visitor); DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}); DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty}); List <DiagnosticsNode> describeMissingAncestor({ @required Type expectedAncestorType }); DiagnosticsNode describeOwnershipChain(String name); }
限于篇幅,去掉了原注释,添加了简短的中文注释。是不是有些方法看着很眼熟?ancestorWidgetOfExactType
、inheritFromWidgetOfExactType
等方法,我们都经常在 build
方法中使用吧?是的,因为 Widget.build(BuildContext context)
中的 context
,指的就是这个 Widget
对应的 Element
,而 Element
又实现了 BuildContext
,那这一切就理所当然了。对于 Widget
的上层组件,由于都比较简单,这里就不细说了,大家可以看看源码。
看一下 Element
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 abstract class Element extends DiagnosticableTree implements BuildContext { Element (Widget widget) : assert (widget != null ), _widget = widget; Element _parent; @override bool operator ==(Object other) => identical(this , other); dynamic get slot => _slot; dynamic _slot; int get depth => _depth; int _depth; static int _sort(Element a, Element b) {} @override Widget get widget => _widget; Widget _widget; @override BuildOwner get owner => _owner; BuildOwner _owner; bool _active = false ; void reassemble() {} RenderObject get renderObject {} void visitChildren(ElementVisitor visitor) { } Element updateChild(Element child, Widget newWidget, dynamic newSlot) {} void mount(Element parent, dynamic newSlot) {} void update(covariant Widget newWidget) {} void updateSlotForChild(Element child, dynamic newSlot) {} void detachRenderObject() {} void attachRenderObject(dynamic newSlot) {} Element inflateWidget(Widget newWidget, dynamic newSlot) {} void deactivateChild(Element child) {} void forgetChild(Element child); void activate() {} void deactivate() {} void unmount() {} RenderObject findRenderObject() => renderObject; Size get size {}; Map <Type , InheritedElement> _inheritedWidgets; Set <InheritedElement> _dependencies; bool _hadUnsatisfiedDependencies = false ; InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {} InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {} InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {} void _updateInheritance() { _inheritedWidgets = _parent?._inheritedWidgets; } Widget ancestorWidgetOfExactType(Type targetType) {} State ancestorStateOfType(TypeMatcher matcher) {} State rootAncestorStateOfType(TypeMatcher matcher) {} RenderObject ancestorRenderObjectOfType(TypeMatcher matcher) {} void visitAncestorElements(bool visitor(Element element)) {} void didChangeDependencies() {} bool get dirty => _dirty; bool _dirty = true ; bool _inDirtyList = false ; void markNeedsBuild() { _dirty = true ; owner.scheduleBuildFor(this ); } void rebuild() {} void performRebuild(); }
很长的 API,但主要是处理_slot/_active/_dirty/_parent 等。现在还比较乱,等结合上层 api 时再细看。
看一下常用到的 StatelessElement
:
1 2 3 4 5 6 7 8 9 10 11 12 13 class StatelessElement extends ComponentElement { StatelessElement(StatelessWidget widget) : super (widget); @override StatelessWidget get widget => super .widget; @override Widget build() => widget.build(this ); @override void update(StatelessWidget newWidget) {} }
简单地重写了几个方法。StatefulWidget
呢:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class StatefulElement extends ComponentElement { StatefulElement(StatefulWidget widget) : _state = widget.createState(), super (widget) { _state._element = this ; _state._widget = widget; } @override Widget build() => state.build(this ); State<StatefulWidget> get state => _state; State<StatefulWidget> _state; void reassemble() {} void update(StatefulWidget newWidget) {} void activate() {} void deactivate() {} void unmount() {} InheritedWidget inheritFromElement(Element ancestor, { Object aspect }) {} void didChangeDependencies() {} }
其他 Element
先不看了,继续看 RenderObject
。
RenderObject
是何时创建的呢?首先我们需要知道的是,虽然对于用户来说,编写的基本都是 Widget
,但对于框架来说,三者建立连接的地方是 Element
。何以见得呢?因为细看上面的 Element
简版,发现它持有了 _widget
和 _renderObject
。在 Element.update
方法中,更新了_widget
;在 RenderObjectElement.mount
方法中,更新 _renderObject
。好,接下来简单看一下 RenderObject
是做什么的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void reassemble() {} ParentData parentData; void setupParentData(covariant RenderObject child) {} void adoptChild(RenderObject child) {} void dropChild(RenderObject child) {} void visitChildren(RenderObjectVisitor visitor) { } PipelineOwner get owner => super .owner; void attach(PipelineOwner owner) {} bool _needsLayout = true ; RenderObject _relayoutBoundary; bool _doingThisLayoutWithCallback = false ; Constraints get constraints => _constraints; Constraints _constraints; void markNeedsLayout() {} void markParentNeedsLayout() {} void markNeedsLayoutForSizedByParentChange() {} void scheduleInitialLayout() {} void layout(Constraints constraints, { bool parentUsesSize = false }) {} bool get sizedByParent => false ; void performResize(); void performLayout(); bool get isRepaintBoundary => false ; bool get alwaysNeedsCompositing => false ; ContainerLayer get layer {} set layer(ContainerLayer newLayer) {} ContainerLayer _layer; bool _needsCompositingBitsUpdate = false ; void markNeedsCompositingBitsUpdate() {} bool _needsCompositing; bool get needsCompositing {} bool _needsPaint = true ; void markNeedsPaint() {} void scheduleInitialPaint(ContainerLayer rootLayer) {} void replaceRootLayer(OffsetLayer rootLayer) {} Rect get paintBounds; void paint(PaintingContext context, Offset offset) {} void applyPaintTransform(covariant RenderObject child, Matrix4 transform) {} Matrix4 getTransformTo(RenderObject ancestor) {} void handleEvent(PointerEvent event, covariant HitTestEntry entry) {} bool hitTest(HitTestResult result, { Offset position }) {} }
来看一下 RenderObject
的继承关系:
由此我们知道所有的实体 RenderObject
都是 RenderBox
类型。来看一下 RenderBox
是做了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 abstract class RenderBox extends RenderObject { void setupParentData(covariant RenderObject child) {} double getMinIntrinsicWidth(double height) {} double computeMinIntrinsicWidth(double height) {} bool get hasSize => _size != null ; Size get size {} Size _size; set size(Size value) {} double getDistanceToActualBaseline(TextBaseline baseline) {} double getDistanceToBaseline(TextBaseline baseline, { bool onlyReal = false }) {} double computeDistanceToActualBaseline(TextBaseline baseline) {} BoxConstraints get constraints => super .constraints; void markNeedsLayout() {} void performResize() {} void performLayout() {} bool hitTest(BoxHitTestResult result, { @required Offset position }) {} bool hitTestSelf(Offset position) => false ; bool hitTestChildren(BoxHitTestResult result, { Offset position }) => false ; void applyPaintTransform(RenderObject child, Matrix4 transform) {} Offset globalToLocal(Offset point, { RenderObject ancestor }) {} Offset localToGlobal(Offset point, { RenderObject ancestor }) {} Rect get paintBounds => Offset.zero & size; void handleEvent(PointerEvent event, BoxHitTestEntry entry) {} }
看完这三大基础后,可能仍然一头雾水?我们直接看看构建流程,这样最清晰了。
在上篇中,说到:
1 2 3 4 5 6 7 void attachRootWidget(Widget rootWidget) { _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( container: renderView, debugShortDescription: '[root]' , child: rootWidget, ).attachToRenderTree(buildOwner, renderViewElement); }
这里调用了 attachToRenderTree
这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 RenderObjectToWidgetElement<T> attachToRenderTree( BuildOwner owner, [ RenderObjectToWidgetElement<T> element ] ) { if (element == null ) { owner.lockState(() { element = createElement(); assert (element != null ); element.assignOwner(owner); }); owner.buildScope(element, () { element.mount(null , null ); }); } else { element._newWidget = this ; element.markNeedsBuild(); } return element; }
当首次 mount 时(即 element 为 null), 这时先通过 createElement
创建一个 element,该 element 作为顶层 element。然后通过 buildScope 方法(构建一个壳,防止潜在的无限循环),传入一个 element.mount(null, null);
作为 callback。然后对所有 dirtyElement 进行 rebuild
操作。直接看代码:
1 2 3 4 5 6 @override void mount(Element parent, dynamic newSlot) { super .mount(parent, newSlot); _rebuild(); }
根据继承关系,向上调用了一连串的 mount
方法。主要操作有,在 RenderObjectElement
层:
1 2 3 4 5 6 7 void mount(Element parent, dynamic newSlot) { super .mount(parent, newSlot); _renderObject = widget.createRenderObject(this ); attachRenderObject(newSlot); _dirty = false ; }
这里会创建对应的 RenderObject
,并且 attach 对应的 slot(attachRenderObject 里会有代理组件处理 RenderObject 的逻辑,这个后面单独说)。上面说 Widget
分类时,说到了 RenderObjectWidget
(就是其下有单儿子、多儿子、叶节点的那个)。这里的 widget
就是 RenderObjectWidget
类型。 在根 Element
层,到达了 mount
方法的终点,这里上面说过了,就是设置一些内部值,比如_parent/_slot 等,以及标志依赖组件。到此结束了 mount
。回到 RenderObjectToWidgetElement._rebuild
方法。
在 _rebuild
中,只是调用了:
1 _child = updateChild(_child, widget.child, _rootChildSlot);
到了 Element.updateChild
方法了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Element updateChild(Element child, Widget newWidget, dynamic newSlot) { if (newWidget == null ) { if (child != null ) deactivateChild(child); return null ; } if (child != null ) { if (child.widget == newWidget) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); return child; } if (Widget.canUpdate(child.widget, newWidget)) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); return child; } deactivateChild(child); } return inflateWidget(newWidget, newSlot); }
其实先说一下官方注释,便于理解:
该方法是组件系统的核心。它会在我们要去增加、更新、删除一个 widget 时调用。 如果 child
为空,newWidget
不为空,就创建一个以 newWidget
作为配置的新的 Element
; 如果 newWidget
为空,child
不为空,就移除 child
; 如果都不为空,如果通过了 Widget.canUpdate
,则用 newWidget
去更新 child
,否则移除 child
,并创建一个新的 Element
; 都为空,则什么都不做。
先简单看一下,移除、更新、创建的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @protected void deactivateChild(Element child) { child._parent = null ; child.detachRenderObject(); owner._inactiveElements.add(child); } @protected void updateSlotForChild(Element child, dynamic newSlot) { void visit(Element element) { element._updateSlot(newSlot); if (element is ! RenderObjectElement) element.visitChildren(visit); } visit(child); } @mustCallSuper void update(covariant Widget newWidget) { _widget = newWidget; } @protected Element inflateWidget(Widget newWidget, dynamic newSlot) { final Key key = newWidget.key; if (key is GlobalKey) { final Element newChild = _retakeInactiveElement(key, newWidget); if (newChild != null ) { newChild._activateWithParent(this , newSlot); final Element updatedChild = updateChild(newChild, newWidget, newSlot); return updatedChild; } } final Element newChild = newWidget.createElement(); newChild.mount(this , newSlot); return newChild; }
精妙之处就在 inflateWidget
中的这个 mount
,它递归调用了所有 widget 对应的 element 的 mount
,由此生成一颗 Element
树。最终返回了顶层元素,赋给 _renderViewElement
。至此,attachRootWidget
方法就返回了。下一步是安排渲染,即:scheduleWarmUpFrame
。这一块是某一篇文章说过的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void scheduleWarmUpFrame() { if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) return ; _warmUpFrame = true ; final bool hadScheduledFrame = _hasScheduledFrame; Timer.run(() { handleBeginFrame(null ); }); Timer.run(() { handleDrawFrame(); resetEpoch(); _warmUpFrame = false ; if (hadScheduledFrame) scheduleFrame(); }); }
通过全局查找 [addPersistentFrameCallback],定位到它的回调为:
1 2 3 4 5 6 7 void drawFrame() { pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); pipelineOwner.flushPaint(); renderView.compositeFrame(); pipelineOwner.flushSemantics(); }
即一次屏幕刷新的全流程:
布局
更新合成位
绘制
合成帧
语意化
简单先看一下布局的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void flushLayout() { if (!kReleaseMode) { Timeline.startSync('Layout' , arguments: timelineWhitelistArguments); } try { while (_nodesNeedingLayout.isNotEmpty) { final List <RenderObject> dirtyNodes = _nodesNeedingLayout; _nodesNeedingLayout = <RenderObject>[]; for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) { if (node._needsLayout && node.owner == this ) node._layoutWithoutResize(); } } } finally { if (!kReleaseMode) { Timeline.finishSync(); } } }
就是遍历 _nodesNeedingLayout
。那么这些脏节点什么时候加上的呢?我翻看了之前 RenderObjectToWidgetElement.mount
方法,也只是构建 Element
树,并没有任何设置脏节点的逻辑。那么这个逻辑是在哪加的呢?答案是在 RendererBinding
的 initInstances
方法里,调用了 initRenderView
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void initRenderView() { renderView = RenderView(configuration: createViewConfiguration(), window : window ); renderView.scheduleInitialFrame(); } void scheduleInitialFrame() { scheduleInitialLayout(); scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer()); owner.requestVisualUpdate(); } void scheduleInitialLayout() { _relayoutBoundary = this ; owner._nodesNeedingLayout.add(this ); } void scheduleInitialPaint(ContainerLayer rootLayer) { _layer = rootLayer; owner._nodesNeedingPaint.add(this ); }
可以看到在初始化 FlutterBinding 的时候,就将根 RenderObject 放入脏节点了,所以后面第一次更新肯定是包含该脏节点的。
但有一个问题(尚未验证):这里 initInstances
方法中就请求了更新视图,为什么还需要调用 scheduleWarmUpFrame
来触发更新呢?是不是首次渲染多了一次??这里记个 TODO
继续看布局:
1 2 3 4 5 6 7 8 9 10 11 12 void _layoutWithoutResize() { RenderObject debugPreviousActiveLayout; try { performLayout(); markNeedsSemanticsUpdate(); } catch (e, stack) { _debugReportException('performLayout' , e, stack); } _needsLayout = false ; markNeedsPaint(); }
关键点是 performLayout
和 markNeedsPaint
。下层 RenderBox 也没有实现 performLayout
,我们随便找一个单儿子的 RenderObject 来看一下。
1 2 3 4 5 6 7 8 9 10 11 @override void performLayout() { if (child != null ) { child.layout(_limitConstraints(constraints), parentUsesSize: true ); size = constraints.constrain(child.size); } else { size = _limitConstraints(constraints).constrain(Size.zero); } }
是不是有点奇怪,为什么要给儿子布局,而不是给自己布局?这就需要了解 flutter 布局这一层的东西了。flutter 中布局相关的东西,主要是 parentData.offset,size。每个 RenderObject 有自己的 parentData
。看名字也好理解:爸爸的数据。所以它是父 RenderObject 给儿子设置的数据。那么通常需要设置哪些数据呢?offset
就是其中之一,它代表儿子在爸爸这里的偏移(水平和垂直方向)。每一个 RenderObject 还有一个 size 属性,表示自己这个渲染对象占据的屏幕尺寸。由上面的代码,可得:先把儿子布好局,安排好(儿子又会递归地给儿子的儿子布局..)后,就知道儿子的大小了。一般场景下,爸爸会和儿子的大小相关,比如上面的把爸爸的大小设置为与儿子大小成限制关系。我理解的布局就是递归地设置 offset
和 size
,这也正好符合 RenderBox
的名称:所有渲染对象都是盒子,盒子不就是一层嵌套一层,A 盒子大小多少,B 盒子大小多少,B 盒子的儿子 B1 盒子大小、相对于 B 盒子的偏移… 渲染对象仅包含布局相关相关(当然,也能绘制,比如背景色、自定义图形等),更多的数据还是保存在 Element
中。毕竟 Element
是沟通 Widget
和 RenderObject
的桥梁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void markNeedsPaint() { if (_needsPaint) return ; _needsPaint = true ; if (isRepaintBoundary) { if (owner != null ) { owner._nodesNeedingPaint.add(this ); owner.requestVisualUpdate(); } } else if (parent is RenderObject) { final RenderObject parent = this .parent; parent.markNeedsPaint(); } else { if (owner != null ) owner.requestVisualUpdate(); } }
很明显,不管是 markNeedsLayout 还是 paint,都需要给 owner 对应的队列添加当前元素。还有一个主要的操作是生成合成层:
1 2 3 4 5 6 7 8 9 10 11 12 void compositeFrame() { try { final ui.SceneBuilder builder = ui.SceneBuilder(); final ui.Scene scene = layer.buildScene(builder); if (automaticSystemUiAdjustment) _updateSystemChrome(); _window .render(scene); scene.dispose(); } finally { Timeline.finishSync(); } }
至此,数据就呈现到屏幕上了。
结束 大致讲解了 Widget
/Element
/RenderObject
的关系和作用。