本篇文章主要用文字描述构建过程,不会插入太多源码。希望读者可以边阅读边结合源码去看看具体实现。
flutter 框架中 widget 的构建过程是由 element 对象负责的。这里还有一个对象 buildOwner 需要指出。这个对象负责管理 widgets framework。flutter 初始化构建和重新构建(rebuild,如 setState 方法触发)过程大体相同都是从 buildOwner 对象的 buildScope 方法开始,初始化过程会先调用 element.mount 方法,构建 flutter 的三颗树(Widget、Element、RenderObject)。
下面根据不同类型的 Widget 分析构建过程。
1. RenderObjectToWidgetAdapter
RenderObjectToWidgetAdapter 是 RenderObjectWidget,这里单独拿出来先说是因为它是我们 Flutter APP 启动 Widget 树构建的起始 Widget。
WidgetsBinding.attachRootWidget 构造 RenderObjectToWidgetAdapter 实例时,会向其构造函数传递 renderView 对象(RendererBinding 在初始化时调用 initRenderView 方法创建)。接着调用 renderObjectToWidgetAdapter.attachToRenderTree 方法,向其传入 buildOwner 对象(WidgetsBinding 初始化时创建)。attachToRenderTree,附加到渲染树,那么具体是把什么附加到那个树上呢?
attachToRenderTree 方法,创建了自己的 element,给 element 分配所有者,然后开始构建 owner.buildScope。buildScope 方法会先执行传入的回调,然后 rebuild _dirtyElements。我们先看这个回调,具体执行 element.mount,这里的 element 是 RenderObjectToWidgetElement。mount 方法内部调用 super.mount 我们跟着看到 RenderObjectElement.mount 方法。这里创建了自己的 renderObject(具体实现在 RenderObjectToWidgetAdapter.createRenderObject,返回了 container,这个 container 就是构造 RenderObjectToWidgetAdapter 时传递的 renderView),然后调用 attachRenderObject 方法准备将自己的 renderObject(renderView)附加到什么地方。这里没有做什么事情,因为 renderView 是 render tree 的根节点。
到这里,我们有 RenderObjectToWidgetAdapter、RenderObjectToWidgetElement、RenderObject(renderView)。
2. StatelessWidget
runApp 方法需要接收一个 Widget 类型参数,这里我们先看个比较简单的 StatelessWidget。
假设我们有一个 MyStatelessWidget extends StatelessWidget,它只需要实现一个 build 方法就可以了,这里随便返回个 Container 吧。
第一部分内容讲到了 RenderObjectToWidgetElement 的 mount 方法,mount 方法最后调用了 _rebuild 方法,内部调用了 updateChild 方法,这里传入了两个参数 _child 为 null,widget.child 是什么呢,是调用 runApp 方法传递的 rootWidget 参数,这里指我们的 MyStatelessWidget。进入 updateChild 方法根据条件内部调用 inflateWidget。这里 Element 的 infalteWidget 方法做了两件事情:1. 创建 Widget 对应的 Element,2. 调用对应 element 对象的 mount 方法。这里createElement 方法调用 StatelessWidget.createElement 方法返回一个 StatelessElement。mount 方法带着 Element parent 参数,这里是 RenderObjectToWidgetElement,在 super.mount 方法中用 _parent 指向了parent。注意在调用 updateChild 方法时,我们用 _child 参数接收其返回值。
mount 方法最后会嵌套调用 _firstBuild -> rebuild -> performRebuild 方法。StatelessElement.performRebuld 方法具体实现在 ComponentElement 里。performBuild 方法主要做两件事:1. 调用 build 方法获取 widget 对象,2. updateChild。build 方法返回 widget.build(this),这里就是调用到了 MyStatelessWidget.build 方法,这也是我们继承 StatelessWidget 必须实现的方法,注意这里传递了参数 this,StatelessWidget build 方法的 BuildContext context 参数就是这个 StatelessWidget 对应的 StatelessElement。updateChild 方法我们前边说了,内部会调用 inflateWidget,创建 element,调用 element.mount。
到这里,我们有了 MyStatelessWidget、和它对应的 StatelessElement。其中 MyStatelessWidget 被 RenderObjectToWidgetAdapter.child 引用。MyStatelessWidget 对应的 StatelessElement 的 _parent 属性指向 renderObjectToWidgetElement,同时后者的 _child 属性指向这个 StatelessElement。
3. StatefulWidget
考虑我们的 MyStatelessWidget 的 build 方法实际返回一个 MyStatefulWidget extends StatefulWidget。MyStatefulWidget 需要实现 createState 方法返回自己对应的 State 对象,这里说 _MyStatefulWidgetState 吧。
接上部分,StatelessElement.inflateWidget 方法会调用 MyStatefulWidget.createElement 方法,StatefulWidget.createElement 创建了 StatefulElement 对象,注意,在 StatefulElement 构造函数初始化列表创建了 StatefulWidget 对应的 State 对象,_state = widget.createState()。然后调用 StatefulElement.mount 方法(在 ComponentElement 里),这里会调用 StatefulElement._firstBuild 方法,_firstBuild 方法里依次调用了 _state.initState 和 _state.didChangeDependencies 方法。然后调用 rebuild -> performRebuild。又到了 performRebuild,内部的 build 方法返回 _state.build(this),然后 updateChild -> inflateWidget -> createElement -> element.mount。
这里我们有了MyStatefulWidget,这里的 MyStatefulWidget 并没有与 MyStatelessWidget 有明确的父子关系(没有 parent/child 属性指定),以及 MyStatefulWidget 对应的 StatefulElement,这里的 StatefulElement 会同 StatelessElement 一样与父 Element 对象建立明确的父子关系。
4. InheritedWidget
InheritedWidget 初学者不太会接触,但是涉及到高级点的知识熟悉 InheritedWidget 是必须的能力。应用主题、状态管理 package provider 等就是用的 InheritedWidget 的知识。
这里我们不具体指定一个 InheritedWidget 直接说 InheritedWidget 这个抽象类吧。
InheritedWidget.createElement 返回一个 InheritedElement 对象,InheritedElement extends ProxyElement 它的 build 方法直接返回 widget.child ,可见,正如其名,做代理。InheritedElement 的 mount 过程与 StatelessElement 的 mount 过程基本相似,不过 _updateInheritance 方法自己做了处理。
普通 element 对象的_updateInheritance 方法直接将自己的 _inheritedWidgets 指向父 element 的 _inheritedWidgets。InheritedElement 的 _updateInheritance 方法会根据父 element 的 _inheritedWidgets (不为空)构建自己的 _inheritedWidgets 或者初始化自己的 _inheritedWidgets 然后将自己添加在这个 Map 中。
InheritedWidget 比较重要,这里做一个详解。
5. RenderObjectWidget
再来一个 RenderObjectWidget 吧。前边的讲解基本说明了 Widget tree 和 Element tree 的构建过程,这里看下 RenderObject 是如何挂载到 renderView 的。
第一部分讲到 RenderObjectElement 的 mount 方法中,会调用 attachRenderObject 方法将 Widget 对应创建的 RenderObject 附加到 renderView 上。这里做法是通过 element tree 找到祖先节点中类型为 RenderObjectElement 的 Element,通过 element 对象使用其对应的 renderObject 完成子 RenderObject 的挂载。具体根据 RenderObject 类型有区别:SingleChildRenderObject 会直接用 child 属性指向这个 renderObject 完成挂载,MultiChildRenderObject 会通过以 _firstChild 为头,_lastChild 为尾的双向链表将多个子 RenderObject 关联起来。
6. rebuild 过程
这里的 rebuild 指 setState 后执行的过程。
setState 方法会先执行传给自己的回调,fn(),然后调用对应 _element.markNeedsBuild 标记 element 对象为 _dirty = true,接着通过 owner.shcheduleBuildFor(this) 驱动 Flutter 渲染流水线过程,这里每次只会 rebuild 在 _dirtyElements 列表里的 Element。这里的 owner 对象即为 BuildOwner 对象,它是在 element tree 构建过程中,由父 element 一直传递下来的。shcheduleBuildFor 的具体过程有两步:1. 执行回调 onBuildScheduled,2. 将这个 _dirty element 添加到自己的 _dirtyElements 列表中。onBuildScheduled 这个回调是在 BuildOwner 对象创建后立即赋值的,具体执行函数内部最终调用了 window.scheduleFrame 向 engine 调度一帧。当下一帧到来时 flutter framework 就开始准备渲染新的一帧,具体参考这里。