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的布局和绘制的流程。