Flutter APP绘制过程简析

本文基于Flutter 3.0

Flutter App基于Dart语言编写,提供了一套简单易用的API,可以让开发者在Flutter中快速开发出一个精美的APP。那么在Flutter中是如何绘制一个APP呢,runApp是怎么将我们编写的Widget等添加到手机上的呢?本文简单从Widget,Element,RenderObjet三者的关系来梳理一下Flutter的绘制过程。

让我们运行一个“最”简单的Flutter App,分析一下在这个过程中涉及到的Widget、Element、RenderObject这三个tree的关系。

1
2
3
4
5
6
7
8
9
main() {
runApp(const Center(
// Center非必须,为了让文本居中显得更清晰
child: Text(
"Hello center text!",
textDirection: TextDirection.ltr, // 文本方向
),
));
}

上述代码的效果如下:

flutter_run_app_hello_center_text

让我们使用Flutter DevTools看一下实际生成的Widget Details Tree

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
[root]
>renderObject:RenderView#a00a5

Center
alignment:Alignment.center
widthFactor:null
heightFactor:null
>renderObject:RenderPositionedBox#94e0d

Text
"Hello center text!"
textAlign:null
textDirection:ltr
locale:null
softWrap:null
overflow:null
textScaleFactor:null
maxLines:null
textWidthBasis:null
textHeightBehavior:null

RichText
textDirection:ltr
softWrap:wrapping at box width
maxLines:unlimited
text:"Hello center text!"
renderObject:RenderParagraph#71aa1

可以看到,除了我们在代码里面添加的Center和Text这两个Widget之外,还多出来好几个Widget/RenderObject,当我们仔细查看具体的Widget,可以看到其内部还有XXXElement,BuildOwner之类的字段:

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
Center
alignment:Alignment.center
widthFactor:null
heightFactor:null
renderObject:RenderPositionedBox#94e0d
>_parent:RenderObjectToWidgetElement
_debugReassembleConfig:null
_notificationTree:null
>_slot:Object
_depth:2
>_widget:Center
>_owner:BuildOwner
>_lifecycleState:_ElementLifecycle
>_debugForgottenChildrenWithGlobalKey:_HashSet
_inheritedWidgets:null
_dependencies:null
_hadUnsatisfiedDependencies:true
_dirty:false
_inDirtyList:false
_debugBuiltOnce:false
_debugA1lowIgnoredCallsToMarkNeedsBuild:false
_debugDoingBuild:false
>_ancestorRenderObjectElement:RenderObjectToWidgetElement
>_child:StatelessElement

上述涉及到的几个类彼此之间到底是什么关系,我们的“Hello center text!”又是怎样才显示到屏幕上的,让我们接下来一个一个分析一下:

runApp

在执行runApp的时候主要执行了三步

1
2
3
4
5
6
7
8
9
10
// -> \lib\src\widgets\binding.dart

void runApp(Widget app) {
// 创建render tree的根节点RenderView
WidgetsFlutterBinding.ensureInitialized()
// 将我们的app widget绑定到RenderView
..scheduleAttachRootWidget(app)
// 安排屏幕帧绘制
..scheduleWarmUpFrame();
}

WidgetsFlutterBinding.ensureInitialized()

创建RenderView具体的逻辑在WidgetsFlutterBinding.ensureInitialized方法中:

1
2
3
4
5
6
7
8
9
10
// -> \lib\src\widgets\binding.dart

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

static WidgetsBinding ensureInitialized() {
if (WidgetsBinding._instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}

ensureInitialized方法中,如果WidgetsBinding._instance为null则会先用构造方法创建,因为WidgetsFlutterBinding继承自BindingBase,所以实际上执行下方的方法:

1
2
3
4
5
6
7
8
9
10
11
12
// -> lib\src\foundation\binding.dart
abstract class BindingBase {
BindingBase() {
initInstances();
initServiceExtensions();
}

@protected
@mustCallSuper
void initInstances() {
}
}

这里主要做了2件事,我们关注initInstances()方法,这个方法的主要逻辑都在他的子类中,也就是之前WidgetsFlutterBinding混合的几个BindingBase子类中,我们关注和屏幕渲染有关的RendererBinding:

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
43
44
45
46
47
// -> lib\src\rendering\binding.dart

/// The glue between the render tree and the Flutter engine.
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
// 这里创建了PipelineOwner,用来管理rendering pipeline也就是我们app中所有的RenderObject
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
platformDispatcher
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
// 注意这里创建了RenderView
initRenderView();
_handleSemanticsEnabledChanged();
assert(renderView != null);
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
if (kIsWeb) {
addPostFrameCallback(_handleWebFirstFrame);
}
}

/// Creates a [RenderView] object to be the root of the
/// [RenderObject] rendering tree, and initializes it so that it
/// will be rendered when the next frame is requested.
///
/// Called automatically when the binding is created.
void initRenderView() {
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}

set renderView(RenderView value) {
assert(value != null);
// 注意这里,将renderView设置为_pipeline的根节点
_pipelineOwner.rootNode = value;
}
}

我们主要关注两件事:

  • 创建了用于管理渲染管道的PipelineOwner _pipelineOwner

    Pipeline是用来管理rendering tree,其内部持有我们的renderView作为rootNode,同时维护了_nodesNeedingLayout,_nodesNeedingCompositingBitsUpdate,_nodesNeedingPaint,_nodesNeedingSemantics四个列表,当flutter framework每次需要往屏幕上绘制内容时会依次遍历这四个列表,将RenderObject绘制到屏幕上面。

  • 创建了rendering tree的根节点renderView ,并将其设置为_pipelineOwner的根节点

..scheduleAttachRootWidget(app)

此方法是WidgetsFlutterBinding的另外一个混合类WidgetsBinding负责具体实现:

WidgetsBindingscheduleAttachRootWidget 方法最后调用了attachRootWidget(Widget rootWidget)

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
// -> lib\src\widgets\binding.dart

/// The glue between the widgets layer and the Flutter engine.
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {

// 将rootWidget绑定到renderViewElement
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
// 用于将rootWidget绑定到renderView上面
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
)
// 在此创建或者使用已有的RenderObjectToWidgetElement,并作为根Element
// 并将RenderObjectToWidgetAdapter和RenderView与之绑定
// 这里的_buildOwner在WidgetsBinding.initInstances方法创建,用于管理widget框架的类
.attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
// 如果是启动框架,就安排更新帧
SchedulerBinding.instance.ensureVisualUpdate();
}
}
}

这里主要有3步:

  • 创建RenderObjectToWidgetAdapter包装RenderView
  • attachToRenderTree方法中创建RenderObjectToWidgetElement并mount到element tree中(widget tree实际上并不存在,而是通过element tree管理)
  • 需要的话安排一次frame(刷新页面)

还需要注意一个新的角色buildOwner,这个对象全局唯一(一般由parent传给child),在WidgetsBinding.initInstances方法创建,用来管理与Widget tree相关的类,实际上就是通过管理Element的插入,移除,更新来间接管理Widget tree(对应我们在之前遇到的用来管理rendering tree的pipelineOwner ,这两个Owner管理着我们所说的Flutter的Widget/Element/RenderObject“三”个tree)。

RenderObjectToWidgetAdapter

前面我们知道renderView其实是一个RenderObject,所以这里为他创建了一个对应的Widget——RenderObjectToWidgetAdapter,其主要作用是将rootWidget(也就是我们最开始写的Center Widget及其child)绑定到之前生成的renderView上面,并将renderView作为自己对应的RenderObject。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// -> lib\src\widgets\binding.dart

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
/// Creates a bridge from a [RenderObject] to an [Element] tree.
///
/// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
RenderObjectToWidgetAdapter({
this.child,
required this.container,
this.debugShortDescription,
}) :
// 注意这里用container也就是RenderView创建了一个GlobalObjectKey,
// 在RenderObjectToWidgetElementmount的时候会用到
super(key: GlobalObjectKey(container));

@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
}

RenderObjectToWidgetAdapter.createRenderObject 返回的就是container 也就是我们的RenderView。

attachToRenderTree

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// -> lib\src\widgets\binding.dart
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
owner.lockState(() {
// 创建RenderObjectToWidgetElement,并将RenderObjectToWidgetAdapter与之绑定
element = createElement();
assert(element != null);
// 创建好Element之后,将BuildOwner与之绑定
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
// 这里最终会通过updateChild方法将rootWidget对应的Element插入到
// RenderObjectToWidgetElement下面,在rootWidget中第一个RenderObjectElement
// 的mount方法中,通过attachRenderObject(newSlot)将自己的renderObject绑定到renderView
element!.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
}

RenderObjectToWidgetAdapterattachToRenderTree 方法中,创建对应的RenderObjectToWidgetElement 与自己绑定,并且同时也将rootWidget和之前创建的rendering tree的根节点renderView绑定。

1
|— RenderObjectToWidgetAdapter   —|— RenderObjectToWidgetElement  —|— RenderView —|

我们再来看一下RenderObjectToWidgetElement调用的父类RenderObjectElement.mount方法:

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
// -> lib\src\widgets\framework.dart

abstract class RenderObjectElement extends Element {

@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
// 本例中这里实际上获取到的是RenderView
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
// 将RenderView绑定到指定newSlot(这里是null)中
attachRenderObject(newSlot);
_dirty = false;
}

@override
void attachRenderObject(Object? newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
// 这里因为RenderView是根节点,所以_ancestorRenderObjectElement和parentDataElement都为null
// 但是对于RenderView下级的节点,比如本例中的Center Widget,他对应的祖先节点就是持有RenderView
// 的RenderObjectToWidgetElement,所以这里会将CenterWidget的RenderPositionedBox
// 作为RenderView的child
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>);
}
}

此外,还会调用的Element.mount方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// -> lib\src\widgets\framework.dart
// Element的方法:

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;
}
assert(owner != null);
// 这里将RenderObjectToWidgetElement注册到owner中,key是创建RenderObjectToWidgetAdapter时候创建的GlobalObjectKey
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
_updateInheritance();
attachNotificationTree();
}

可以看到,这里将RenderObjectToWidgetElement 注册到了BuildOwner中

RenderObjectToWidgetElementmount方法执行时,除了调用父类的mount方法外,还会触发_rebuild() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {{
Element? _child;
void _rebuild() {
try {
// 在这里分析可知,这里的widget即`RenderObjectToWidgetElement` 的widget,也就是
// `RenderObjectToWidgetAdapter`,他的child也就是rootWidget
// 所以updateChild传入的值分别是null,RenderObjectToWidgetAdapter.child
// 会创建rootWidget对应的element并将其作为当前element的child
_child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
} catch (exception, stack) {
...
}
}

_rebuild方法中,我们可以看到,在WidgetsBinding.attachRootWidget方法中给RenderObjectToWidgetAdapter作为child参数传入的rootWidget(也即我们示例中的CenterWidget),在这里被传入了RenderView对应的RenderObjectToWidgetElement的child中(这里的过程我们下面Center一节再分析),从而将其插入到Flutter的渲染树中。

这样RenderView(RenderObject)就有了对应的WidgetElement,并且有了自己的child

..scheduleWarmUpFrame()

这个方法则是尽快安排一个frame以便在屏幕下次刷新的时候显示app的内容(在app启动之后的第一次!!!),这样我们的app启动了,我们写的内容也能正常显示到屏幕上。

通过上述分析,我们可以得知,runApp方法执行之后,创建了RenderView对象,并将其作为整个Flutter APP的RenderObject rendering tree的根节点(后续所有的Widget创建的RenderObject都是在RenderView的下层),并且初始化它以便在下一帧的时候对其进行渲染。


分析完了runApp,我们再来看一下刚刚提到的几个类,以及他们是如何添加到我们的flutter app中的。

RenderView

先看一下在最顶层的RenderView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
↓[root]
>renderObject:RenderView#a00a5
parent:null
_debugReassembleConfig:null
_notificationTree:nul1
slot:null
depth:1
_widget:RenderObjectToWidgetAdapter
>_owner:BuildOwner
_lifecycleState:_ElementLifecycle
_debugForgottenChildrenWithGlobalKey:_HashSet
_inheritedWidgets:nul1
_dependencies:null
_hadUnsatisfiedDependencies:false
_dirty:false
_inDirtyList:false
_debugBuiltOnce:false
_debugA1lowIgnoredCallsToMarkNeedsBuild:false
_debugDoingBuild:false
_ancestorRenderObjectElement:null
_child:SingleChildRenderObjectElement
_newWidget:null

查阅源码可知,RenderView是RenderObject,一般情况下是Flutter的根View,表示整个rendering tree的output surface,处理引导着render pipeline。

RenderView有且仅有一个RenderBox类型的child,他会强制将childsize改为RenderView初始化时候的入参configuration的值(一般是当前window也就是手机屏幕的逻辑像素size)。

Center

上节我们说道,Center Widget通过RenderObjectToWidgetElement.updateChild(最终调用Element同名方法)方法插入到渲染树中,下面我们详细分析一下这个过程:

updateChild中,因为child==null,而newWidget也就是Center不为null,所以直接使用inflateWidget(newWidget, newSlot)创建新的Element并作为RenderObjectToWidgetElement的_child,而作为第一次创建的Center,在Element.inflateWidget方法中大概会执行下面几步:

1
2
3
4
5
6
// -> lib\src\widgets\framework.dart
// Element的inflateWidget方法:

final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;

也就是这里先执行了Center.createElement方法创建Element,然后调用此Element.mount方法将Element添加到Element tree。

让我们再看一下Center的Widget Details Tree:

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
Center
alignment:Alignment.center
widthFactor:null
heightFactor:null
renderObject:RenderPositionedBox#94e0d
>_parent:RenderObjectToWidgetElement
_debugReassembleConfig:null
_notificationTree:null
>_slot:Object
_depth:2
>_widget:Center
>_owner:BuildOwner
>_lifecycleState:_ElementLifecycle
>_debugForgottenChildrenWithGlobalKey:_HashSet
_inheritedWidgets:null
_dependencies:null
_hadUnsatisfiedDependencies:true
_dirty:false
_inDirtyList:false
_debugBuiltOnce:false
_debugA1lowIgnoredCallsToMarkNeedsBuild:false
_debugDoingBuild:false
>_ancestorRenderObjectElement:RenderObjectToWidgetElement
>_child:StatelessElement

可以看到Center的_parent_ancestorRenderObjectElement是RenderObjectToWidgetElement,_depth是2,这个和我们最初的分析一致,因为Center(其实严格来说,是Center Widget的(或子级的)RenderObject)是RenderView的child

我们接下来主要关注一下几个属性:

  • alignment: Alignment.center
  • renderObject: RenderPositionedBox
  • _widget: Center
  • _child: StatelessElement

先看一下Center的源码:

1
2
3
4
5
6
7
// -> lib\src\widgets\basic.dart

class Center extends Align {
/// Creates a widget that centers its child.
const Center({ Key? key, double? widthFactor, double? heightFactor, Widget? child })
: super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}

Center代码十分简单,主要的逻辑在他的父类Align中:

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
// -> lib\src\widgets\basic.dart

class Align extends SingleChildRenderObjectWidget {
const Align({
Key? key,
// 这里alignment默认是居中
this.alignment = Alignment.center,
this.widthFactor,
this.heightFactor,
Widget? child,
}) : super(key: key, child: child);

@override
RenderPositionedBox createRenderObject(BuildContext context) {
// Center的父类可以创建自己的RenderObject
return RenderPositionedBox(
alignment: alignment,
widthFactor: widthFactor,
heightFactor: heightFactor,
textDirection: Directionality.maybeOf(context),
);
}

@override
void updateRenderObject(BuildContext context, RenderPositionedBox renderObject) {
renderObject
..alignment = alignment
..widthFactor = widthFactor
..heightFactor = heightFactor
..textDirection = Directionality.maybeOf(context);
}
}

Align其实是一个SingleChildRenderObjectWidget ,对应的Element是SingleChildRenderObjectElement,他创建的RenderObject是RenderPositionedBox

SingleChildRenderObjectElement 是一个RenderObjectElement 也就意味着他在rendering tree有一个关联的RenderObject负责layout,painting以及hit-test。

回到我们的Center Widget中:

alignment: Alignment.center

Alignment.center是在创建Center的时候默认设置的对齐方式

renderObject: RenderPositionedBox

RenderPositionedBox是Center Widget对应的RenderObject,在SingleChildRenderObjectWidget.mount 的时候创建。其本身并不在屏幕上绘制肉眼可见的内容,而是将child按照指定的对齐方式进行定位。

RenderPositionedBox 的继承关系:RenderPositionedBoxRenderAligningShiftedBoxRenderShiftedBoxRenderBoxRenderObject

RenderPositionedBox可以按照给定的AlignmentGeometry定位child。在本例中,他的几个属性如下:

  • alignment: Alignment.center
  • _owner: PipelineOwner
  • _parent: RenderView
  • _child: RenderParagraph

前三个属性含义很明显,这里我们注意到他的_child并不是我们预期的Text,这个原因我们后面再分析。

_widget: Center

其实通过上述的分析,我们应该已经知道,我们在Widget Details Tree中看到的Center其实是Center Widget对应的Element,也就是SingleChildRenderObjectElement

其继承关系:SingleChildRenderObjectElementRenderObjectElementElement

根据Element的定义,这里的Widget是在Widget创建SingleChildRenderObjectElement的时候传入的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// -> lib\src\widgets\framework.dart

Element(Widget widget)
: assert(widget != null),
_widget = widget;

// -> lib\src\widgets\framework.dart
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const SingleChildRenderObjectWidget({ Key? key, this.child }) : super(key: key);

/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final Widget? child;

@override
SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
}

然后这个Element在上述WidgetsBinding.attachRootWidget步骤中通过一系列操作,最终在RenderObjectToWidgetElement的updateChild方法被创建并被BuildOwner 插入到tree中。

这里的_widget才真正对应着我们在runApp里面传入的Center Widget,他的child也正是我们的Text。

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
Center
alignment:Alignment.center
widthFactor:null
heightFactor:null
>renderObject:RenderPositionedBox#6e802
>_parent:RenderObjectToWidgetElement
_debugReassembleConfig:null
_notificationTree:null
_slot:Object
denth:2 _
[[[widget:Center]]] //注意这里
key:null
location:_Location
[[[child:Text]]] //注意这里
key:null
>_location:_Location
data:'Hello center text!'
textSpan:null
style:null
strutStyle:null
textAlign:null
>textDirection:TextDirection
locale:null
softWrap:null
overflow:null
textScaleFactor:null
maxLines:null
semanticsLabel:null
textWidthBasis:null
textHeightBehavior:null
>alignment:Alignment
widthFactor:null
heightFactor:nul1
>_owner:BuildOwner

_child: StatelessElement

Center对应的Element的_child是一个StatelessElement,按照我们上一步的分析,StatelessElement应该是Text Widget创建,事实也确实如此:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
↓_child: StatelessElement
>_parent: SingleChildRenderObjectElement
debugReassembleConfig: null
_notificationTree: null
slot: null
depth: 3
>_widget: Text
>_owner: BuildOwner
>_lifecycleState: _ElementLifecycle
>_debugForgottenChildrenWithGlobalKey: _HashSet
_inheritedWidgets: null
dependencies: null
_hadUnsatisfiedDependencies: true
_dirty: false
_inDirtyList: false
debugBuiltOnce: false
_debugAllowIgnoredCallsToMarkNeedsBuild: false
>_child: MultichildRenderObjectElement
debugDoingBuild: false

让我们分析一下这个_child的赋值过程:

1
2
3
4
5
6
7
8
// -> lib\src\widgets\framework.dart
class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_child = updateChild(_child, (widget as SingleChildRenderObjectWidget).child, null);
}
}

在Center对应的Element——SingleChildRenderObjectElementmount的时候,同时也会更新child(本例中Center的child是Text),这里调用的是Element的updateChild方法,他的逻辑如下:

1
2
3
4
|                     | **newWidget == null**  | **newWidget != null**   |
| :-----------------: | :--------------------- | :---------------------- |
| **child == null** | Returns null. | Returns new [Element]. |
| **child != null** | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |

updateChild的逻辑分为4种情况:其余情况都比较简单,只有newWidget != null或者child != null的时候需要判断一下,如果可以更新就更新否则就创建新的Element,可以分为下面这几种情况:

  • child.widget == newWidget:两个是同一个对象,就只更新childslot
  • Widget.canUpdate(child.widget, newWidget):二者的runtimeTypekey一样,就调用child.update(newWidget)更新child._widget,必要时更新childslot
  • 否则创建新的element并替代

到这里跟Center插入到render tree的步骤一样,将Text插入到了tree中。

Text

接下来我们分析一下Text是如何被加入Widget Details Tree的。

其继承关系:TextStatelessWidgetWidget

Text是StatelessWidget,他的内容比较简单,主要的逻辑都在build方法中:

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
// -> \lib\src\widgets\text.dart

class Text extends StatelessWidget {

const Text(
String this.data, {
Key? key,
...
}) :
textSpan = null,
super(key: key);

@override
Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
TextStyle? effectiveTextStyle = style;
if (style == null || style!.inherit)
effectiveTextStyle = defaultTextStyle.style.merge(style);
if (MediaQuery.boldTextOverride(context))
effectiveTextStyle = effectiveTextStyle!.merge(const TextStyle(fontWeight: FontWeight.bold));
// 注意这里返回了RichText
Widget result = RichText(
textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
...
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ? <InlineSpan>[textSpan!] : null,
),
);
...
return result;
}
}

同样,作为StatelessWidget,他也创建了一个StatelessElement

其继承关系:StatelessElementComponentElementElement

按照之前的分析,Text插入到tree中执行的方法分别是Text.createElement和Text对应的Element——StatelessElement.mount方法:

Text是StatelessWidget的子类,他的主要逻辑都在StatelessWidget:

1
2
3
4
5
6
7
8
9
// -> lib\src\widgets\framework.dart

abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key? key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}

可以看到其createElement创建的是StatelessElement,也就是说Text插入到Center过程主要在StatelessElement中。

StatelessElement.mount方法主要逻辑在ComponentElement中,这个方法除了调用Element同名方法外,还调用了ComponentElement._firstBuild()Element.rebuild()ComponentElement.performRebuild()

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
// -> lib\src\widgets\framework.dart
// ComponentElement类中的方法

void performRebuild() {
Widget? built;
try {
// 这里调用Element对应的Widget的build方法创建Widget,也就是RichText
built = build();
} catch (e, stack) {
...
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
}
// 将上述创建的Widget:built经过处理后赋值给Element的_child
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(...);
_child = updateChild(null, built, slot);
}
}

这里的主要有两个步骤:

  • 调用ComponentElement.build方法,生产Widget(本例中,间接调用了Text的build方法)
  • 调用ComponentElement.updateChild方法,更新child(最终执行的是Element同名方法逻辑)

到目前为止,我们的Widget/Element/RenderObject tree如下(第四级RichText下面再分析):

1
2
3
4
5
6
7
8
9
**Widget**:       RenderObjectToWidgetAdapter → Center                         → Text              → *RichText*

**Element**: RenderObjectToWidgetElement → SingleChildRenderObjectElement → StatelessElement → *MultiChildRenderObjectElement*

**RenderObject**: RenderView → RenderPositionedBox → [X] → *RenderParagraph*

注:
1. 这里的[X]实际上不存在,只是为了和Text对应表示这里本应该有一个对应的RenderObject
2. 最后一列*RichText*对应的节点目前还没有分析到,此处仅做提前展示

不难看出,在从定往下数第三层也就是Text对应的这一级中,RenderObject tree这里并没有对应的对象,在上面的分析中,我们也看到了StatelessWidget本身并没有创建RenderObject的方法。实际上,Widget分为多个种类,只有RenderObject类及其子类才会创建RenderObject:

Untitled

RichText

终于到了我们这个app真正在屏幕上显示的内容这里了,上面我们分析到,Text作为StatelessWidget,本身并不会产生可以在屏幕上绘制的RenderObject,而是通过他的build方法返回一个可以产生RenderObject的Widget,在本例中,这个Widget就是RichText:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// -> lib\src\widgets\basic.dart

class RichText extends MultiChildRenderObjectWidget {
@override
RenderParagraph createRenderObject(BuildContext context) {
assert(textDirection != null || debugCheckHasDirectionality(context));
return RenderParagraph(text,
textAlign: textAlign,
textDirection: textDirection ?? Directionality.of(context),
softWrap: softWrap,
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
locale: locale ?? Localizations.maybeLocaleOf(context),
);
}
}

RichText继承自MultiChildRenderObjectWidget ,如上节分析的,是一种RenderObjectWidget,它创建了真正在屏幕上渲染的RenderObject——RenderParagraph

1
2
3
4
5
6
// -> lib\src\rendering\paragraph.dart

class RenderParagraph extends RenderBox
with ContainerRenderObjectMixin<RenderBox, TextParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, TextParentData>,
RelayoutWhenSystemFontsChangeMixin {}

上面说道,Text本身作为StatelessWidget并不产生RenderObject,那么这里的RenderParagraph是如何找到并插入到rendering tree中的呢?

带着这个疑问,我们看一下MultiChildRenderObjectWidget 创建的MultiChildRenderObjectElement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// -> lib\src\widgets\framework.dart

class MultiChildRenderObjectElement extends RenderObjectElement {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
final MultiChildRenderObjectWidget multiChildRenderObjectWidget = widget as MultiChildRenderObjectWidget;
// 本例中不涉及children
final List<Element> children = List<Element>.filled(multiChildRenderObjectWidget.children.length, _NullElement.instance);
Element? previousChild;
for (int i = 0; i < children.length; i += 1) {
final Element newChild = inflateWidget(multiChildRenderObjectWidget.children[i], IndexedSlot<Element?>(i, previousChild));
children[i] = newChild;
previousChild = newChild;
}
_children = children;
}
}

这里可以看到,在MultiChildRenderObjectElement的mount方法中:

  • 调用父类mount方法
  • 遍历了所有的children将其插入到MultiChildRenderObjectElement中。

在前面的Text源码中,我们注意到给只给RichText.text赋值了,RichText的textSpanchildren都是null,所以后面对children的处理在本例中不涉及,让我们看一下super.mount(parent, newSlot)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
// -> lib\src\widgets\framework.dart

abstract class RenderObjectElement extends Element {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
// 这里调用对应的RenderObjectWidget创建_renderObject
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
// 将其绑定到rendering tree中
attachRenderObject(newSlot);
_dirty = false;
}
}

这里主要有2步:

  • 通过widget.createRenderObject 创建_renderObject,本例中就是用RichText创建了RenderParagraph
  • 调用RenderObjectElement.attachRenderObject方法将_renderObject插入到rendering tree

让我们看一下attachRenderObject的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// -> lib\src\widgets\framework.dart
// RenderObjectElement类的方法
@override
void attachRenderObject(Object? newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
// 向上遍历,找到父级节点中最近的RenderObjectElement
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
// 将renderObject插入
_ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
final ParentDataElement<ParentData>? parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget as ParentDataWidget<ParentData>);
}

RenderObjectElement? _findAncestorRenderObjectElement() {
Element? ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement)
ancestor = ancestor._parent;
return ancestor as RenderObjectElement?;
}

可以看到,在attachRenderObject方法中插入的方式很简单:先在当前tree中向上找到父级中离得最近的RenderObjectElement,在本例中是Center这个Widget对应的SingleChildRenderObjectElement(注意不是创建RichText的Text),然后调用其insertRenderObjectChild方法将当前的RenderParagraph插入到rendering tree中:

1
2
3
4
5
6
7
8
// -> lib\src\widgets\framework.dart
class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void insertRenderObjectChild(RenderObject child, Object? slot) {
final RenderObjectWithChildMixin<RenderObject> renderObject = this.renderObject as RenderObjectWithChildMixin<RenderObject>;
renderObject.child = child;
}
}

在SingleChildRenderObjectElement的insertRenderObjectChild方法中先是查找当前Element持有的renderObject,然后将我们传入的RichText的RenderObject——RenderParagraph赋值给renderObject.child

到这里,我们的所有Widget在Element的组织下,将对于的RenderObject添加到Rendering Tree中,他们的关系如下:

1
2
3
4
5
**Widget**:       RenderObjectToWidgetAdapter → Center                         → Text              → RichText

**Element**: RenderObjectToWidgetElement → SingleChildRenderObjectElement → StatelessElement → MultiChildRenderObjectElement

**RenderObject**: RenderView → RenderPositionedBox → RenderParagraph

这样,当屏幕刷新的时候,这些内容便绘制在屏幕上面。

总结

runApp方法中,WidgetsFlutterBinding作为将flutter framework绑定到flutter engine的粘合剂:

  • ensureInitialized方法中创建了_pipelineOwner(管理rendering tree)、renderViewbuildOwner(通过管理Element tree间接管理widget tree),并将renderView设置为_pipelineOwner的根节点。

  • scheduleAttachRootWidget方法中,为renderView创建并绑定了对应的Widget(RenderObjectToWidgetAdapter)和Element(RenderObjectToWidgetElement)。然后通过RenderObjectToWidgetElement.mount方法,将之前创建的buildOwner与自己绑定。

    并且将我们在runApp传入的WidgetrootWidget(也就是本例中的Center Widget)对应的Element添加为RenderObjectToWidgetElement的子节点。并依此将Text、Text内部的RichText等对应的Element都加入到Element tree中,直到遍历完整个Widget tree。

  • scheduleWarmUpFrame方法中安排在下一次屏幕刷新的时候将我们的内容展示在屏幕上面。

下面是我们这个“最”简单的Flutter App的结构示意:

参考资料

Flutter - Dart API docs

Flutter, what are Widgets, RenderObjects and Elements? - Norbert Kozsir | Flutter Europe

Flutter Widgets Explained | Understand How Flutter Works!

深入浅出 Flutter Framework 之 PipelineOwner