Flutter三棵树

下面是Flutter官网提供的关于三棵树的介绍,比较形象,这里直接摘录了,可以参考原文

1
2
3
4
5
6
7
8
9
Container(
color: Colors.blue,
child: Row(
children: [
Image.network('https://www.example.com/1.png'),
const Text('A'),
],
),
);

上面这段代码构建的三棵树如下所示:

Flutter三棵树示意图

在开发调试阶段使用Flutter inspector可以看出实际的Widget树要比代码中描述的层级更深。

Widget树

先看下Widget的家族:

WidgetRenderObjectWidgetSingleChildRenderObjectWidgetMultiChildRenderObjectWidgetFlexColumnRowStatefulWidgetStatelessWidgetInheritedWidgetProxyWidgetContainer

两外还有如下经常使用的Wiget:

  • PreferredSizeWidget继承自Widget,相关子类有AppBar和TabBar;
  • LayoutBuilder可以根据parent的布局约束来创建合理的布局;

StatelessWidget

StatelessWidgetStatelessElement createElement()Widget build(BuildContext context)ElementComponentElementWidgetStatelessElement

StatefulWidget

WidgetElement createElement()StatefulWidgetStatefulElement createElement()State createState()StateT extends StatefulWidgetStatefulElementElementComponentElementwidget.createState

RenderObjectWidget源码分析

BuildContextWidgetElementRenderObjectRenderObjectWidgetRenderObjectElement createElementRenderObject createRenderObject()ContainerLayerLayerPictureLayerRenderObjectElementLayerHandle

Element树

先看下Element的家族:

BuildContextWidget get widget;ElementRenderObjectElementRenderObjectWidgetComponentElementLeafRenderObjectElementSingleChildRenderObjectElementMultiChildRenderObjectElement

从类图可以看出Element是连接Widget和RenderObject的纽带。

ComponentElementComponentElementStatefulElementStatefulElementStateStateElementElementStatefulWidgetStatefulWidgetmount_firstBuildinitState_firstBuildrebuildperformRebuilddidChangeDependenciesperformRebuildbuildbuildupdateChild是widget系统的核心方法inflateWidgetcreateElementnewmountmount

RenderObject树

通常情况下RenderObject是由RenderObjectElement创建的,当Element状态变为unmounted时,要销毁RenderObject(调用RenderObject.dispose)。我们一般不会定义RenderObject的子类,而是使用RenderBox(或者RenderProxyBox),RenderBox采用的是笛卡尔坐标系,如果需要创建不是笛卡尔坐标系的布局,则就需要直接继承RenderObject了。

RenderObjectRenderObject? _parent;ParentData? parentData;RenderBoxRenderProxyBoxRenderSliverRenderProxySliverRenderShiftedBoxRenderFlex

大部分的 Flutter widget 是由一个继承了 RenderBox 的子类的对象渲染的,它们呈现出的 RenderObject 会在二维笛卡尔空间中拥有固定的大小。 RenderBox 提供了 盒子限制模型,为每个 widget 关联了渲染的最小和最大的宽度和高度。

  • RenderParagraph继承自RenderBox,用于渲染文本;
  • RenderImage也是继承自RenderBox,用于渲染图片;
  • RenderTransform继承自RenderProxyBox,用于在绘制子节点内容前应用变换;
  • RenderView是render tree的根节点,直接继承自RenderObject。RenderView只有一个孩子节点RenderBox。
  • RenderProxyBoxWithHitTestBehavior继承自RenderProxyBox,我们自定义widget是可以用这个,这个组件允许自定义hit-testing行为,比如HitTestBehavior.deferToChild;

三棵树的生成过程

RootWidget创建过程


zenuml
WidgetsBinding.wrapWithDefaultView {

}
WidgetsBinding.scheduleAttachRootWidget {
    "异步执行attachRootWidget"
}
WidgetsBinding.attachRootWidget {
    new RootWidget
    attachToBuildOwner {
        RootWidget.attach {
            new RootElement
            RootElement.assignOwner
            BuildOwner.buildScope {
                RootElement."callback执行mount" {
                    _rebuild {
                        "Element.updateChild"
                    }
                }
                "首次build _dirtyElements为空"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void runApp(Widget app) { //app就是用户组装的widget配置信息
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
//1. wrapWithDefaultView(app)会给app在加一个parent,这个parent就是View,View继承自StatelessWidget
//2. scheduleAttachRootWidget异步调用attachRootWidget(rootWidget)
//3. scheduleWarmUpFrame立马进行帧刷新,而不会等待Vsync信号
binding
..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
..scheduleWarmUpFrame();
}

//WidgetsBinding
void attachToBuildOwner(RootWidget widget) {
final bool isBootstrapFrame = rootElement == null;
_readyToProduceFrames = true;
//RootWidget.attach会创建RootElement,并执行RootElement.mount,RootElement的parent为null
_rootElement = widget.attach(buildOwner!, rootElement as RootElement?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}

Element的根是RootElement,RootElement的mount就是创建Element树的起点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null) {
//1. newWidget为空,child不为空,则将child从树中删除
//deactivateChild(child)会执行下面语句:
//a) child._parent = null; 从Element树中删除
//b) child.detachRenderObject(); //render树中删除
//c) owner!._inactiveElements.add(child); 添加到列表中最终会执行child.deactivate()
deactivateChild(child);
}
//2. newWidget和child都为空,直接返回
return null;
}

final Element newChild;
if (child != null) { //3. newWidget和child都不为空
if (child.widget == newWidget) { //3.1 widget是相同的实例,比如用const修饰的Widget
if (child.slot != newSlot) {
//只有具备多个child的Element才给child分配slot,只有单个child的slog始终为null。
//比如MultiChildRenderObjectElement.mount方法中会给每个child生成一个IndexedSlot
updateSlotForChild(child, newSlot);
}
newChild = child; //复用原来的Element
} else if (Widget.canUpdate(child.widget, newWidget)) { //3.2 判断runtimeType和key是否都相同
if (child.slot != newSlot) {
updateSlotForChild(child, newSlot);
}
//newWidget会赋值给Element._widget,不同Element还有自己的实现:
//* SingleChildRenderObjectElement.update中会继续执行updateChild
//* MultiChildRenderObjectElement.update中执行updateChildren,会对比新旧两个列表,然后根据情况执行更新、删除、插入等操作
child.update(newWidget);
newChild = child;
} else { //3.3 widget变成了完全不同的widget
deactivateChild(child); //从树中删除老的Element和RenderObject
newChild = inflateWidget(newWidget, newSlot);
}
} else { //4. newWidget不为空,child为空,要添加新的child
newChild = inflateWidget(newWidget, newSlot);
}

return newChild;
}

在RootWidget首次执行mount时,走的是第4分支(newWidget不为空,child为空)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Element inflateWidget(Widget newWidget, Object? newSlot) {
try {
final Key? key = newWidget.key;
if (key is GlobalKey) {
//取出GlobalKey关联的Element,如果还未关联或者Widget.canUpdate为false,则返回null。否则Element会从原来的树种摘除
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
try {
newChild._activateWithParent(this, newSlot);
} catch (_) {
......
}
//GlobalKey关联的Element可以复用,则执行updateChild
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
return updatedChild!;
}
}
//创建新Element,比如StatefulWidget.createElement()
final Element newChild = newWidget.createElement();
//新的Element挂载到parent(this)上
newChild.mount(this, newSlot);

return newChild;
} finally {...}
}

Element.mount

下面是Element.mount的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void mount(Element? parent, Object? newSlot) {
_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;
}
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
_updateInheritance(); //_inheritedElements = _parent?._inheritedElements;
attachNotificationTree();
}

Element.mount默认实现是更新_parent/_slot等。除了默认实现外,其他子类Element都会有自己的实现。

ComponentElement

ComponentElement是一个组合类,本身不会产生RenderObject。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//ComponentElement
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_firstBuild();
}

void _firstBuild() { //StatefulElement会重写_firstBuild来执行didChangeDependencies
rebuild(); // 最终会调用performRebuild.
}
void performRebuild() {
Widget? built;
try {
built = build(); //执行StatelessWidget.build或者State<StatefulWidget>.build,用来创建一个newWidget传给updateChild
} catch (e, stack) { ... }
try {
//将新build的newWidget
_child = updateChild(_child, built, slot);
} catch (e, stack) { ... }
}

SingleChildRenderObjectElement

SingleChildRenderObjectElement继承自RenderObjectElement,会创建自己的RenderObject。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//SingleChildRenderObjectElement
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); //RenderObjectElement.mount
_child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
}
//RenderObjectElement
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); //调用Element.mount,更新_parent等信息
_renderObject = (widget as RenderObjectWidget).createRenderObject(this); //创建RenderObject
//attachRenderObject会把新创建的RenderObject挂载到render tree中;
//但当前Element的parent不一定是RenderObjectElement,因此会从Element树中向上找最近的RenderObjectElement进行挂载,
attachRenderObject(newSlot);
super.performRebuild(); // clears the "dirty" flag
}

MultiChildRenderObjectElement

MultiChildRenderObjectElement也是继承自RenderObjectElement,有多个孩子Element。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@override
void mount(Element? parent, Object? newSlot) {
//调用父类RenderObjectElement.mount,会创建RenderObject,并挂载到render tree
super.mount(parent, newSlot);
final MultiChildRenderObjectWidget multiChildRenderObjectWidget = widget as MultiChildRenderObjectWidget;
final List<Element> children = List<Element>.filled(multiChildRenderObjectWidget.children.length, _NullElement.instance);
Element? previousChild;
//遍历widget children,比如Column/Row的children
for (int i = 0; i < children.length; i += 1) {
//inflateWidget的作用是为child widget创建Element,并执行Element.mount
//新建一个IndexedSlot,第一个参数是index,第二个参数是前一个兄弟Element
final Element newChild = inflateWidget(multiChildRenderObjectWidget.children[i], IndexedSlot<Element?>(i, previousChild));
children[i] = newChild; //child elment放到children数组中
previousChild = newChild;
}
_children = children;
}