前言
Flutter知识体系中,Widget、Element以及RenderObject是比较重要的三个类族;里面的知识点十分多,今天侧重于给大家分享这三棵树是如何构建出来的。
类族继承关系
只列举一些和本文有关的
Widget:
通过上面的图可以知道,Widget基类定义了createElement()
函数,子类通过override该方法构造不同的element类;并且还需要重点关注的是,只有RenderObjectWidget类才定义createRenderObject()
函数,所以,能构造出renderObject的都只能是其子类;其次,可以发现只有StatelessWidget类才有build()
函数(StatefulWidget会通过state调用build()
),这个在后面会说到。
Element:
从这里可以看到,element和widget的各个类都基本是对应的
RenderObject:
Widget/Element/RenderObject的初始化过程,以及各自的关系
首先定位到main.dart的入口函数:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
复制代码
WidgetsFlutterBinding主要负责连接Flutter框架层和engine层,这里通过我们传入的Widget生成Wiget/Elemtn/RenderObject树,其他关于WidgetsFlutterBinding这里就不过多去讨论,在这我们只需要知道该类有两个重要的属性,分别是Render Tree的根节点,Element Tree的根节点;以及初初始化根节点的方法:
RenderObjectToWidgetAdapter _renderViewElement // 继承自RenderObjectWidget
RenderView renderView // 继承自RenderObject
// 初始化rootElement和rootWidget,并把rootRenderObject绑定到rootElement上,把我们写的Widget(app)绑定到rootWidget上
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
}
复制代码
接下来,我们来看一下class RenderObjectToWidgetAdapter的定义:
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
final Widget child;
final RenderObjectWithChildMixin<T> container;
RenderObjectWidget {
RenderObjectToWidgetAdapter({
this.child,
this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
// 把自己作为element构造函数的参数,生成一个element实例
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element
if (element == null) {
...
// 没有rootElement就实例化一个
element = createElement();
// 通过mount函数,实例化一个rootRenderObject并挂载到rootElement上
element.mount(null, null);
...
} else {
...
}
// 返回rootElement给外部WidgetsFlutterBinding的_renderViewElement赋值
return element;
}
}
复制代码
通过上面的代码可以知道,RenderObjectToWidgetAdapter类就是一个适配器,通过它构造出了整个树
我们先来看一下根节点是如何初始化生成的,简要如下:
- RenderObjectToWidgetAdapter通过构造函数获取了外部传入的widget和renderObject(rootRenderObject),初始化一个rootWidget的实例对象,并把外部传入的widget挂载到rootWidget上
- RenderObjectToWidgetAdapter的实例对象通过
createElement()
创建一个RenderObjectToWidgetElement的实例对象作为rootElement,rootElement持有rootWidget和rootRenderObject. - rootElement的调用
mount()
函数,在父类的实现中,会通过一个getter方法,获取当前rootElement持有的widget,也就是rootWidget,然后rootWidget调用createRenderObject()
方法返回从外部获取的renderObject,并挂载到rootElement,作为rootRenderObject - 通过上面的步骤,三棵树的根节点就创建好,并建立了相互的引用关系
子节点是如何初始化生成的,简要如下:
- rootElement在
mount()
函数最后,继续调用了自己的私有函数_rebuild()
,通过源码可以看到,rootElement的_child就被构造完成。
void _rebuild() {
try {
_child = updateChild(_child, widget.child, _rootChildSlot);
assert(_child != null);
} catch (exception, stack) {
...
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
复制代码
- 这里的
updateChild()
函数是一个重点,看其源码可以知道,这个函数主要是通过参数child和newWidget的组合判断返回结果,如下:
newWidget == null | newWidget != null | |
---|---|---|
child == null | Return:null. | Return new [Element] |
child != null | 移除child,Return:null | return child or new [Element] |
由于这里我们只讨论第一次初始化,所以这里传入的child == null, newWidget是已挂载到rootWidget上的widget,所以会通过inflateWidget()
函数初始化一个newChild,并返回挂载到rootElement上。注意,这里的newChild初始化后,并不是直接返回的,而是继续调用其mount()函数,通过super.mount()
,这里需要注意的是,ComponentElement和RenderObjectElement是不一样的,RenderObjectElement会初始化一个RenderObject挂载到Element上。整个过程就是这样不断的查找widget的child构造出相对应的element和renderObject.
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
Element newChild;
if (child != null) {
...
} else {
newChild = inflateWidget(newWidget, newSlot);
}
...
return newChild;
}
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
...
final Element newChild = newWidget.createElement();
...
newChild.mount(this, newSlot);
...
return newChild;
}
复制代码
结束
脑袋嗡嗡的…
文章篇幅有限,到这里就结束了,下次再继续聊Flutter.
有错误的地方,请各位大佬不吝指正,共同学习,一起提高。