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 的关系。
main() {
runApp(const Center(
// Center 非必须,为了让文本居中显得更清晰
child: Text(
"Hello center text!",
textDirection: TextDirection.ltr, // 文本方向
),
));
}
上述代码的效果如下:
让我们使用 Flutter DevTools 看一下实际生成的 Widget Details Tree
[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 之类的字段:
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 的时候主要执行了三步
// -> \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
方法中:
// -> \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,所以实际上执行下方的方法:
// -> lib\src\foundation\binding.dart
abstract class BindingBase {
BindingBase() {
initInstances();
initServiceExtensions();
}
@protected
@mustCallSuper
void initInstances() {
}
}
这里主要做了 2 件事,我们关注initInstances()
方法,这个方法的主要逻辑都在他的子类中,也就是之前WidgetsFlutterBinding
混合的几个 BindingBase 子类中,我们关注和屏幕渲染有关的 RendererBinding:
// -> 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
负责具体实现:
WidgetsBinding
的scheduleAttachRootWidget
方法最后调用了attachRootWidget(Widget rootWidget)
:
// -> 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。
// -> 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
// -> 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!;
}
}
RenderObjectToWidgetAdapter
在attachToRenderTree
方法中,创建对应的RenderObjectToWidgetElement
与自己绑定,并且同时也将rootWidget
和之前创建的 rendering tree 的根节点renderView
绑定。
|— RenderObjectToWidgetAdapter —|— RenderObjectToWidgetElement —|— RenderView —|
我们再来看一下RenderObjectToWidgetElement
调用的父类RenderObjectElement.mount
方法:
// -> 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
方法:
// -> 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 中
在RenderObjectToWidgetElement
的mount
方法执行时,除了调用父类的mount
方法外,还会触发_rebuild()
方法:
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
(也即我们示例中的Center
Widget),在这里被传入了 RenderView 对应的 RenderObjectToWidgetElement 的child
中(这里的过程我们下面 Center 一节再分析),从而将其插入到 Flutter 的渲染树中。
这样 RenderView(RenderObject
)就有了对应的Widget
和Element
,并且有了自己的child
。
..scheduleWarmUpFrame()
这个方法则是尽快安排一个 frame 以便在屏幕下次刷新的时候显示 app 的内容(在 app 启动之后的第一次!!!),这样我们的 app 启动了,我们写的内容也能正常显示到屏幕上。
通过上述分析,我们可以得知,runApp 方法执行之后,创建了RenderView
对象,并将其作为整个 Flutter APP 的 RenderObject rendering tree 的根节点(后续所有的 Widget 创建的 RenderObject 都是在 RenderView 的下层),并且初始化它以便在下一帧的时候对其进行渲染。
分析完了runApp
,我们再来看一下刚刚提到的几个类,以及他们是如何添加到我们的 flutter app 中的。
RenderView
先看一下在最顶层的RenderView:
↓[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
,他会强制将child
的size
改为 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
方法中大概会执行下面几步:
// -> 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:
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.centerrenderObject
: RenderPositionedBox_widget
: Center_child
: StatelessElement
先看一下 Center 的源码:
// -> 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 中:
// -> 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
的继承关系:RenderPositionedBox
→ RenderAligningShiftedBox
→ RenderShiftedBox
→ RenderBox
→ RenderObject
RenderPositionedBox可以按照给定的AlignmentGeometry
定位 child。在本例中,他的几个属性如下:
alignment
: Alignment.center_owner
: PipelineOwner_parent
: RenderView_child
: RenderParagraph
前三个属性含义很明显,这里我们注意到他的_child
并不是我们预期的Text
,这个原因我们后面再分析。
_widget: Center
其实通过上述的分析,我们应该已经知道,我们在 Widget Details Tree 中看到的 Center 其实是 Center Widget 对应的 Element,也就是SingleChildRenderObjectElement
。
其继承关系:SingleChildRenderObjectElement
→ RenderObjectElement
→ Element
根据 Element 的定义,这里的 Widget 是在 Widget 创建SingleChildRenderObjectElement
的时候传入的:
// -> 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。
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 创建,事实也确实如此:
↓_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
的赋值过程:
// -> 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——SingleChildRenderObjectElement
在mount
的时候,同时也会更新child
(本例中 Center 的child
是 Text),这里调用的是 Element 的updateChild
方法,他的逻辑如下:
| | **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
:两个是同一个对象,就只更新child
的slot
Widget.canUpdate(child.widget, newWidget)
:二者的runtimeType
和key
一样,就调用child.update(newWidget)
更新child._widget
,必要时更新child
的slot
- 否则创建新的
element
并替代
到这里跟 Center 插入到 render tree 的步骤一样,将 Text 插入到了 tree 中。
Text
接下来我们分析一下 Text 是如何被加入 Widget Details Tree 的。
其继承关系:Text
→ StatelessWidget
→ Widget
Text 是 StatelessWidget,他的内容比较简单,主要的逻辑都在build
方法中:
// -> \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
:
其继承关系:StatelessElement
→ ComponentElement
→ Element
按照之前的分析,Text 插入到 tree 中执行的方法分别是Text.createElement
和 Text 对应的 Element——StatelessElement.mount
方法:
Text 是 StatelessWidget 的子类,他的主要逻辑都在 StatelessWidget:
// -> 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()
:
// -> 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 下面再分析):
**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:
RichText
终于到了我们这个 app 真正在屏幕上显示的内容这里了,上面我们分析到,Text 作为 StatelessWidget,本身并不会产生可以在屏幕上绘制的 RenderObject,而是通过他的build
方法返回一个可以产生 RenderObject 的 Widget,在本例中,这个 Widget 就是 RichText:
// -> 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
:
// -> 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
:
// -> 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 的textSpan
和children
都是null
,所以后面对children
的处理在本例中不涉及,让我们看一下super.mount(parent, newSlot)
方法:
// -> 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 的实现:
// -> 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 中:
// -> 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 中,他们的关系如下:
**Widget**: RenderObjectToWidgetAdapter → Center → Text → RichText
**Element**: RenderObjectToWidgetElement → SingleChildRenderObjectElement → StatelessElement → MultiChildRenderObjectElement
**RenderObject**: RenderView → RenderPositionedBox → RenderParagraph
这样,当屏幕刷新的时候,这些内容便绘制在屏幕上面。
总结
在runApp
方法中,WidgetsFlutterBinding 作为将 flutter framework 绑定到 flutter engine 的粘合剂:
在
ensureInitialized
方法中创建了_pipelineOwner
(管理 rendering tree)、renderView
和buildOwner
(通过管理 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, what are Widgets, RenderObjects and Elements? - Norbert Kozsir | Flutter Europe