有了前面Widget和Element的基础,现在我们大概可以从runApp
方法开始了解一下Flutter的启动流程。
1 | void main() => runApp(MyApp()); |
这是我们初始化Flutter时默认代码,非常简单,就执行了runApp
方法,传入我们app的Widget。
1 | void runApp(Widget app) { |
runApp就只有三行代码,我们一个一个看。
1 | static WidgetsBinding ensureInitialized() { |
ensureInitialized
,从名字就可以看出来,是确保这个对象已经初始化了,返回的是一个WidgetsBinding
对象,这个名字很熟悉了,上一节讲BuildOwner
提到过,BuildOwner
是在WidgetsBinding
中创建的。现在我们又知道WidgetsBinding
是在runApp
时就创建的。
1 | class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { |
WidgetsFlutter
本身没什么逻辑,但是混入了很多个binding
,每个binding
都有自己不同的功能,可以单独使用,也可以像WidgetsFlutter
一样混合使用。
ensureInitialized
返回的是一个WidgetsBinding
实例,我们首先看一下WidgetsBinding
的创建过程。
1 | mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {} |
1 | abstract class BindingBase { |
WidgetsBinding
继承自BingdingBase
,BindingBase
的构造函数中调用了initInstance
方法来生成一个实例。
1 | // WidgetBinding中的实现 |
在这里可以看到,上篇提到的BuildOwner
就是在这时候被创建。
接着runApp
调用了WidgetsBinding.scheduleAttachRootWidget
,并将我们自定义的Widget传入
1 |
|
在这里我们可以看到上篇讲到的Element的流程,在attachToRenderTree
中通过createElement
创建出了Element Tree的根节点,并且将BuildOwner
赋值给根节点,此BuildOwner
将层层传递下去。
之后调用BuildOwner.buildScope
方法,并传入执行根节点mount
方法的回调,执行根节点的mount
最后调用scheduleWarmUpFrame
去渲染第一帧。
小节
通过对runApp
的分析,前面Element的逻辑就更加通畅了,基本了解了从根节点开始的渲染流程。