1.背景说明
做Flutter
开发也有段时间了,在实践中我们清晰的得知Flutter
中一切皆是Widget
,在绝大多数时候,我们就是跟各种StatefulWidget
和StatelessWidget
打交道。Widget
对于我们而言,既是UI布局文件的配置信息,也是一些功能性的实现,如GestureDetector
等。
当然通过简单的学习,我们也会了解到随处可见的Flutter
的三棵树:Widget
树,Element
树,RenderObject
树。抄一张官网的截图如下:
引用一下官网的说明:
既然
Widget
只是描述一个UI元素的配置信息,那么真正的布局、绘制是由谁来完成的呢?Flutter
框架的的处理流程是这样的:
1.根据Widget
树生成一个Element
树,Element
树中的节点都继承自Element
类。
2.根据Element
树生成Render
树(渲染树),渲染树中的节点都继承自RenderObject
类。
3.根据渲染树生成Layer
树,然后上屏显示,Layer
树中的节点都继承自Layer
类。
以上是Flutter
布局绘制的基本架构,那么问题来了,这几棵树是如何被关联起来的?关联起来后又是如何进行布局或绘制的呢?为了讲清楚这个流程,就必须现弄清楚三棵树的事情。本文将为大家先梳理三棵树,之后再写一篇说明布局或绘制。
2.Flutter的三棵树
我们从三棵树开始说起,从一个App的运行开始看看这三棵树是如何建立的。
我们建立一个新的Flutter
工程,得到的入口如下:
void main() => runApp(MyApp());
MyApp()
是我们App的根节点Widget
,整个运行的开始都在runApp()
中。我们从分析runApp()
的实现开始。
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
三个步骤,从命名中可得知:对应的是初始化,挂载根节点Widget
,绘制首帧。我们来看每一步的具体实现。
2.1.WidgetsFlutterBinding.ensureInitialized()
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding._instance == null) {
WidgetsFlutterBinding();
}
return WidgetsBinding.instance;
}
}
WidgetsFlutterBinding
继承BindingBase
,同时混合了GestureBinding
,SchedulerBinding
,ServicesBinding
,PaintingBinding
,SemanticsBinding
,RendererBinding
,WidgetsBinding
。我们看一下其注释:
/// * [GestureBinding], which implements the basics of hit testing.
/// * [SchedulerBinding], which introduces the concepts of frames.
/// * [ServicesBinding], which provides access to the plugin subsystem.
/// * [PaintingBinding], which enables decoding images.
/// * [SemanticsBinding], which supports accessibility.
/// * [RendererBinding], which handles the render tree.
/// * [WidgetsBinding], which handles the widget tree.
这些都是将Framework
和Flutter
引擎进行绑定的桥梁,其中RendererBinding
和WidgetsBinding
与我们的三棵树有关,接下来将对它们进行分析。在此之前,我们先看WidgetsFlutterBinding
的父类BindingBase
的初始化:
BindingBase() {
//....
initInstances();
//....
initServiceExtensions();
//....
}
其中initInstances()
和initServiceExtensions()
都由其子类或mixin
进行实现,其中initServiceExtensions()
是在绑定初始化完成之后去初始化一些服务拓展,这个方法我们先不做展开,只看initInstances()
的实现。
由于我们将要分析的是Flutter
的布局和绘制,所以我们看RendererBinding
和WidgetsBinding
对于initInstances()
的实现。
2.1.1.RendererBinding
@override
void initInstances() {
super.initInstances();
_instance = this;
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
platformDispatcher
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
if (kIsWeb) {
addPostFrameCallback(_handleWebFirstFrame);
}
}
2.1.1.1. PipelineOwner和PlatformDispatcher
首先创建了一个PipelineOwner
对象,PipelineOwner
是用来管理渲染管道的,ensureVisualUpdate()
的实现如下:
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) {
return;
}
assert(() {
if (debugPrintScheduleFrameStacks) {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
}
return true;
}());
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
看起来是用来触发渲染逻辑的。其中platformDispatcher
是个PlatformDispatcher
单例,它是最基础的Flutter Framework
和宿主操作系统之间的接口,管理加载在屏幕硬件上的views,screens列表和显示窗口的配置信息。它的scheduleFrame()
会执行native的方法,其描述如下:
/// Requests that, at the next appropriate opportunity, the [onBeginFrame] and
/// [onDrawFrame] callbacks be invoked.
总而言之就是真正的绘制操作的执行。我们继续看initRenderView()
的实现。
2.1.1.2. RenderView
void initRenderView() {
assert(!_debugIsRenderViewInitialized);
assert(() {
_debugIsRenderViewInitialized = true;
return true;
}());
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}
其中RenderView
继承RenderObject
。这个方法的作用就是为RenderObject
树创建一个作为根节点。
RenderView({
RenderBox? child,
required ViewConfiguration configuration,
required ui.FlutterView window,
}) : assert(configuration != null),
_configuration = configuration,
_window = window {
this.child = child;
}
set renderView(RenderView value) {
assert(value != null);
_pipelineOwner.rootNode = value;
}
set rootNode(AbstractNode? value) {
if (_rootNode == value) {
return;
}
_rootNode?.detach();
_rootNode = value;
_rootNode?.attach(this);
}
@mustCallSuper
void attach(covariant Object owner) {
assert(owner != null);
assert(_owner == null);
_owner = owner;
}
window
是个继承ui.FlutterView
的ui.SingletonFlutterWindow
对象,它是个单例,是Flutter scene真正被绘制的view。它包含自己的Layer
树,在Flutter scene渲染时会将这些Layer
渲染到FlutterView
所在的区域。其中scene
是个native对象,它存储了Layer
结构,是最终真正被渲染的对象。
在创建RenderView
时候,也将自己赋值给_pipelineOwner
的rootNode
。而rootNode
会调用attach()
方法,将_pipelineOwner
赋值给rootNode
的_owner
,总而言之就是相互持有了。我们继续看renderView.prepareInitialFrame()
的实现:
void prepareInitialFrame() {
assert(owner != null);
assert(_rootTransform == null);
scheduleInitialLayout();
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
assert(_rootTransform != null);
}
先看scheduleInitialLayout()
的实现:
void scheduleInitialLayout() {
assert(!_debugDisposed);
assert(attached);
assert(parent is! RenderObject);
assert(!owner!._debugDoingLayout);
assert(_relayoutBoundary == null);
_relayoutBoundary = this;
assert(() {
_debugCanParentUseSize = false;
return true;
}());
owner!._nodesNeedingLayout.add(this);
}
其中_relayoutBoundary
是布局边界,其作用是对布局变化范围进行约束,如果是_relayoutBoundary
,那么它将不会影响其父布局的大小,在之后的章节会进行详细的说明。因为renderView
是根节点的view
,它的布局边界就是它自己。_nodesNeedingLayout
是个List,存储了需要被layout
的view
,这个在之后的章节也会进行说明。我们再看 scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer())
的实现。
2.1.1.3. TransformLayer
TransformLayer _updateMatricesAndCreateNewRootLayer() {
_rootTransform = configuration.toMatrix();
final TransformLayer rootLayer = TransformLayer(transform: _rootTransform);
rootLayer.attach(this);
assert(_rootTransform != null);
return rootLayer;
}
void scheduleInitialPaint(ContainerLayer rootLayer) {
assert(rootLayer.attached);
assert(attached);
assert(parent is! RenderObject);
assert(!owner!._debugDoingPaint);
assert(isRepaintBoundary);
assert(_layerHandle.layer == null);
_layerHandle.layer = rootLayer;
assert(_needsPaint);
owner!._nodesNeedingPaint.add(this);
}
先创建了一个TransformLayer
对象,Layer
就是真正储存到scene
里的绘制内容,先将rootLayer
与renderView
进行了绑定,再赋值给_layerHandle
的layer
对象。_layerHandle
用来阻止Layer
的平台图像资源被回收的handle。_nodesNeedingPaint
也是个List,储存了需要被重新绘制的view,在之后的章节也将进行说明。
我们再次回到initInstances()
方法,看addPersistentFrameCallback(_handlePersistentFrameCallback)
的实现:
void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.add(callback);
}
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_scheduleMouseTrackerUpdate();
}
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
将_handlePersistentFrameCallback
存储在_persistentCallbacks
中。而_handlePersistentFrameCallback
的核心在drawFrame()
上。从drawFrame()
的实现我们可得知,实际上是通过pipelineOwner
去执行layout
,paint
等一些列操作。相关具体实现我们在之后的章节进行分析。_handlePersistentFrameCallback
将在handleDrawFrame()
方法中进行调用,而handleDrawFrame()
其中一个调用时机是在scheduleWarmUpFrame()
中。这个方法看上去非常熟悉,可以回到最初的runApp()
:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
我们找到了这条链路,之后再做分析。到此为止,WidgetsFlutterBinding.ensureInitialized()
的过程就分析完了。我们总结一下:
- 创建了几个重要的对象:管理渲染管道的
PipelineOwner
,将Flutter Framework和宿主操作系统连接起来的PlatformDispatcher
,根节点RenderView
和真正储存到scene
里的绘制内容TransformLayer
。其中RenderView
继承RenderObject
,是我们三棵树之一的RenderObject
树的根节点,是真正处理布局和绘制的对象。 - 再将它们关联起来:将App的根节点
RenderView
的内容存储在TransformLayer
上,交由PipelineOwner
进行调度。PipelineOwner
最终会调用PlatformDispatcher的scheduleFrame()
通知native层进行渲染。
总结成如下示意图:
之前我们提到过,
RendererBinding
和WidgetsBinding
与我们的三棵树有关,我们分析了刚刚分析了RendererBinding
的initInstances()
方法,得到了第一棵RenderObject
树,接下来看一下WidgetsBinding的initInstances()
方法的实现。
2.1.2. WidgetsBinding
@override
void initInstances() {
super.initInstances();
//...
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
//...
void _handleBuildScheduled() {
//...
ensureVisualUpdate();
}
WidgetsBinding
最重要的就是创建了一个BuildOwner
对象,BuildOwner
是用来管理Widget framework的,它有个onBuildScheduled
的callBack
,它的实现是我们上个章节分析过的ensureVisualUpdate()
。而onBuildScheduled
的调用在其scheduleBuildFor(Element element)
中,被Element
的activate()
和markNeedsBuild()
调用。关于BuildOwner
我们将在下一章节scheduleAttachRootWidget(app)
进行说明。
2.2.scheduleAttachRootWidget(app)
2.2.1.Widget树和Element树
先看它的实现:
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
先创建了一个RenderObjectToWidgetAdapter
对象。RenderObjectToWidgetAdapter
继承RenderObjectWidget
,继承Widget
。
RenderObjectToWidgetAdapter({
this.child,
required this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
而它的child
对应的rootWidget
就是我们App的根widget:MyApp()
。container
是我们上一章节分析过的RenderView
。
由此可见这个RenderObjectToWidgetAdapter
其实就是我们三棵树之一:Widget
树的根节点。到此为止,三棵树中的两棵已经明了了,最后一棵Element
树在哪儿呢?_renderViewElement
看上去像是Element
树。那我们继续看attachToRenderTree()
的实现:
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!;
}
RenderObjectToWidgetElement
继承Element
,我们来看createElement()
的实现:
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
实际上就是创建了一个Element
对象,到此为止三棵树都出现了:
-
Widget
树:RenderObjectToWidgetAdapter
对象。 -
Element
树:RenderObjectToWidgetElement
对象。 -
RenderObject
树:RenderView
对象。
我们也可以很清晰的看见RenderObjectToWidgetElement
将RenderObjectToWidgetAdapter
和RenderObject
连接了起来,将这三棵树一一对应。
我们再看element!.assignOwner(owner)
:
void assignOwner(BuildOwner owner) {
_owner = owner;
}
Element
持有了BuildOwner
对象,用来管理dirty element列表。接着分析owner.buildScope()
的实现,代码很长,有两个重要节点:
1.回调callback
,实现element!.mount(null, null)
。mount()
的作用是将当前element
添加到树中parent
节点上。
-
element.rebuild()
:让widget
更新自己。
2.2.2.mount()
我们先看看mount()
方法是怎么将自己挂载到parent
上的:
//RenderObjectToWidgetElement
@override
void mount(Element? parent, Object? newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
assert(_child != null);
}
父类RootRenderObjectElement
的实现:
//RootRenderObjectElement
@override
void mount(Element? parent, Object? newSlot) {
// Root elements should never have parents.
assert(parent == null);
assert(newSlot == null);
super.mount(parent, newSlot);
}
}
父类RenderObjectElement
的实现:
//RenderObjectElement
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(() {
_debugDoingBuild = true;
return true;
}());
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
assert(!_renderObject!.debugDisposed!);
assert(() {
_debugDoingBuild = false;
return true;
}());
assert(() {
_debugUpdateRenderObjectOwner();
return true;
}());
assert(_slot == newSlot);
attachRenderObject(newSlot);
_dirty = false;
}
父类Element
的实现:
//Element
void mount(Element? parent, Object? newSlot) {
assert(_lifecycleState == _ElementLifecycle.initial);
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
assert(slot == null);
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) {
// Only assign ownership if the parent is non-null. If parent is null
// (the root node), the owner should have already been assigned.
// See RootRenderObjectElement.assignOwner().
_owner = parent.owner;
}
assert(owner != null);
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
_updateInheritance();
attachNotificationTree();
}
由于每个子类都是先调用super
方法,所以我们从父类Element
的实现倒着往回看:
首先将_lifecycleState
设置为_ElementLifecycle.active
。将parent
的BuildOwner
对象赋值给每个Element
节点,注册GlobalKey
。然后调用_updateInheritance()
。_updateInheritance()
的实现如下:
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
将父的_inheritedWidgets
赋值给当前Element
节点的_inheritedWidgets
。InheritedWidgets
是通过将组件和InheritedWidgets
的依赖关系层层向下传递,来实现父与子的数据共享的。详见:https://www.jianshu.com/p/926fe12e4437
最后调用attachNotificationTree()
:
@protected
void attachNotificationTree() {
_notificationTree = _parent?._notificationTree;
}
将父的_notificationTree
赋值给当前Element
节点的_notificationTree
。_notificationTree
的作用是让Widget
树的所有节点收到notification
,比如说ListView
滚动相关的notification
,会通过_notificationTree
将scroll
状态层层传递下去。
不过当前我们mount()
方法的传参parent
和newSlot
都是null
,所以只会执行注册GlobalKey
的逻辑。
我们再看子类RenderObjectElement
的重要实现:
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
创建并持有了一个RenderObject
对象:
@protected
@factory
RenderObject createRenderObject(BuildContext context);
其真正的实现在子类里:
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
这个container
是不是很熟悉,它就是我们在创建RenderObjectToWidgetAdapter
时传入的container
,即上一个章节提到的RenderView
。到此为止,Element
是如何持有的RenderObject
就明晰了。
createRenderObject()
完成后,我们再回到RenderObjectElement
,看到最后还有一行重要的实现attachRenderObject(newSlot)
,这个方法的作用是将当前RenderObject
添加到RenderObject
树中相应的位置。在分析完父类mount()
方法的实现后,我们再回到子类RenderObjectToWidgetElement
中继续看mount()
方法的实现。还剩最后一行代码_rebuild()
:
@pragma('vm:notify-debugger-on-exception')
void _rebuild() {
try {
_child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
} catch (exception, stack) {
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: ErrorDescription('attaching to the render tree'),
);
FlutterError.reportError(details);
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
追踪updateChild()
的实现,传参(widget as RenderObjectToWidgetAdapter<T>).child
是我们App的根Widget
:MyApp()
:
@protected
@pragma('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null) {
deactivateChild(child);
}
return null;
}
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
//....
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
//...
child.update(newWidget);
//...
newChild = child;
} else {
deactivateChild(child);
//..
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
//...
return newChild;
}
这个方法可以说是Element
最核心的部分,作为连接Widget
和RenderObject
的桥梁,Widget
作为布局的配置无论如何创建,更新,删除,都会通过Element
的updateChild()
方法去做一遍处理,根据不同条件判断是否需要创建/更新/删除Element
,看看是否有可以复用Element
,从而达到节约资源的目的。它的实现逻辑如下:
-
newWidget
是null
且child
不为null
,则执行deactivateChild()
。deactivateChild()
的实现如下:
@protected
void deactivateChild(Element child) {
assert(child != null);
assert(child._parent == this);
child._parent = null;
child.detachRenderObject();
owner!._inactiveElements.add(child); // this eventually calls child.deactivate()
assert(() {
if (debugPrintGlobalKeyedWidgetLifecycle) {
if (child.widget.key is GlobalKey) {
debugPrint('Deactivated $child (keyed child of $this)');
}
}
return true;
}());
}
将当前Element
放到_inactiveElements
列表中去,并将child Element
和RenderObject
解绑。
- 如果
newWidget
是null
且child
是null
,直接返回null
。 - 如果
newWidget
不为null
且child
不为null
,如果可以的话将会更新旧的child
,返回child
或创建一个新的Element
。
更新旧child
的判断条件是:
hasSameSuperclass && child.widget == newWidget
或者
Widget.canUpdate(child.widget, newWidget)
Widget.canUpdate()
实现如下:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
判断runtimeType
和key
是否相同。除此之外将会执行inflateWidget(newWidget, newSlot)
创建新Element
。
- 如果
newWidget
不为null
且child
是null
,执行inflateWidget(newWidget, newSlot)
创建新Element
。
如此看来inflateWidget()
是个很重要的方法,它的注释如下:
/// Create an element for the given widget and add it as a child of this
/// element in the given slot.
为Widget
创建一个Element
,并将它作为child
添加在当前Element
节点中。看上去这个步骤会将Widget
和Element
进行绑定,我们看看它的实现:
@protected
@pragma('vm:prefer-inline')
Element inflateWidget(Widget newWidget, Object? newSlot) {
//...
final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
if (isTimelineTracked) {
Map<String, String>? debugTimelineArguments;
//...
try {
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);
assert(newChild == updatedChild);
return updatedChild!;
}
}
final Element newChild = newWidget.createElement();
//...
newChild.mount(this, newSlot);
assert(newChild._lifecycleState == _ElementLifecycle.active);
return newChild;
} finally {
//...
}
}
先尝试通过_retakeInactiveElement(key, newWidget)
去创建一个newChild
:
Element? _retakeInactiveElement(GlobalKey key, Widget newWidget) {
final Element? element = key._currentElement;
if (element == null) {
return null;
}
if (!Widget.canUpdate(element.widget, newWidget)) {
return null;
}
//...
final Element? parent = element._parent;
if (parent != null) {
//...
parent.forgetChild(element);
parent.deactivateChild(element);
}
assert(element._parent == null);
owner!._inactiveElements.remove(element);
return element;
}
这个步骤其实就是在通过key
来找是否有可以复用的Element
,它存在上面我们提到的_inactiveElements
列表中。如果有且Widget.canUpdate()
的话就不要创建了,直接从缓存中拿来用即可。返回的newChild
将会作为参数再次执行updateChild()
方法, 这时Widget.canUpdate()
为true
,将不会执行inflateWidget()
方法,而是会更新旧的child
。
如果_retakeInactiveElement()
没有找到可复用的Element
返回为null
,那么将会执行以下代码:
final Element newChild = newWidget.createElement();
就是为Widget
创建相对应的Element
对象。到这里我们终于看到了将Widget
和Element
关联起来的关键代码。最后执行newChild.mount(this, newSlot)
。这个mount
方法在上一章节我们分析过,将child Element
挂载到当前Element
节点上。总结一下:
- 在1.1章节分析
WidgetsFlutterBinding.ensureInitialized()
,我们得到了RenderObject
树的根节点。本章节我们通过scheduleAttachRootWidget(app)
得到了Widget
树和Element
树的根节点,分别是RenderObjectToWidgetAdapter
和RenderObjectToWidgetElement
。 - 通过
(widget as RenderObjectWidget).createRenderObject(this)
将上一个章节提到的RenderView
和当前Element
进行绑定。 - 通过
newWidget.createElement()
将Widget
和Element
关联起来。 - 最后把我们的根
Widget
:MyApp()
通过mount()
方法挂载到树的根节点上。
总结成如下示意图:
scheduleAttachRootWidget(app)
分析完成了,我们最后再看看runApp()
的最后一个执行方法:scheduleWarmUpFrame()
。
2.3.scheduleWarmUpFrame()
void scheduleWarmUpFrame() {
//...
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame();
//...
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame) {
scheduleFrame();
}
});
//...
}
重点在handleDrawFrame()
方法中,这个我们在之前的章节分析过,核心在drawFrame()
即真正的每一帧绘制过程。这个过程我们将在之后的章节进行梳理。
3.总结
通过分析runApp()
方法,我们可得知在执行真正的绘制操作之前会创建好三棵树:Widget
树,Element
树,RenderObject
树,并将三棵树进行关联。同时初始化好渲染管道,发送渲染信号等待真正的渲染过程。有了这基础的三棵树,在下一篇文章将详细分析Flutter
的布局和绘制的流程。