Flutter 页面构建,是先有Element还是先有Widget,是不是有点类似“先有鸡还是先有蛋”的迷惑?
带着这个疑问,打一打断点跟踪下程序源码的执行过程,做了一个简单的记录。
本文秉承多年来的一个写作风格,那就是写的内容不知所云,正如红楼梦中的“满纸荒唐言,一把辛酸泪,都云作者痴,谁解其中味”。
就当作是自己的草稿纸吧。大家别看就是了。
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
继续跟进:
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
1、 attachRootWidget
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
这里创建了一个Widget, 类型为:RenderObjectToWidgetAdapter
这个widget的child 是main里面设置的第一个Widget

2、attachToRenderTree
通过attachToRenderTree 方法,创建了一个Element
类型为RenderObjectToWidgetElement
这个Element的widget指向 RenderObjectToWidgetAdapter这个widget。
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
element!.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
Widget祖先:
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget
abstract class RenderObjectWidget extends Widget
Element祖先:
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement
abstract class RootRenderObjectElement extends RenderObjectElement
abstract class RenderObjectElement extends Element
这么看来,是先有Widget 即:class RenderObjectToWidgetAdapter<T extends RenderObject>
然后才是Element, 即:class RenderObjectToWidgetElement<T extends RenderObject>
我们main函数里面写的第一个根Widget 下面的build创建子widget, 是从Element的mount方法开始的。

3、接着是所有子widget 创建过程。 使用深度优先遍历递归的方式创建所有子节点
3.1、通过 built = build(); 创建新的widget
Widget build() => (widget as StatelessWidget).build(this);
Widget build() => state.build(this);
Widget build() => (widget as ProxyWidget).child;
void performRebuild() {
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
Widget? built;
try {
assert(() {
_debugDoingBuild = true;
return true;
}());
built = build();
assert(() {
_debugDoingBuild = false;
return true;
}());
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
_debugDoingBuild = false;
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
} finally {
_dirty = false;
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
_child = updateChild(null, built, slot);
}
}
3.2、Element.inflateWidget方法中的 final Element newChild = newWidget.createElement(); 创建新的Element
Element inflateWidget(Widget newWidget, Object? newSlot) {
assert(newWidget != null);
try {
final Key? key = newWidget.key;
if (key is GlobalKey) {
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild._activateWithParent(this, newSlot);
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
return updatedChild!;
}
}
final Element newChild = newWidget.createElement();
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild.mount(this, newSlot);
assert(newChild._lifecycleState == _ElementLifecycle.active);
return newChild;
} finally {
}
总结main过程启动如下:
在Flutter中,main函数是整个应用程序的入口,它的执行流程如下:
- 创建一个WidgetsBinding对象,并将其作为参数传递给runApp()方法。
- 调用runApp()方法,并将根Widget作为参数传递给它。
- 创建一个Flutter Engine对象,并将其与Flutter引擎进行连接。
- 启动Flutter引擎,并将Flutter渲染线程、IO线程等线程启动起来。
- 创建一个RenderView对象,并将其作为根RenderObject添加到渲染树中。
- 启动Flutter的消息循环,等待消息的到来。
- 在执行main函数之前,Flutter会先执行一些底层的初始化操作,例如初始化日志系统、初始化渲染引擎、注册系统服务等。这些操作通常由Flutter引擎的C++部分实现。
执行完main函数之后,Flutter会进入消息循环,等待消息的到来。在消息循环中,Flutter会处理各种事件,例如用户输入事件、定时器事件、系统通知等。如果有新的渲染请求,Flutter会根据需要更新渲染树,并将渲染结果显示出来。
需要注意的是,main函数执行的过程通常是异步的,也就是说,Flutter引擎会在后台启动各种线程和服务,并不会阻塞应用程序的主线程。因此,开发者在编写Flutter应用程序时,需要充分考虑异步操作的情况,以避免应用程序出现卡顿或其他异常行为。
在Flutter中,页面创建渲染过程中,Element树、Widget树和RenderObject树是紧密相连的三个树形结构,它们之间的关系可以概括为:
-
Widget树是Element树的逻辑结构,它由一组Widget对象组成,Widget对象描述了页面上的元素应该长成什么样子。在Widget树中,每个Widget对象都有一个对应的Element对象来表示它在页面上的实例。
-
Element树是渲染树的逻辑结构,它由一组Element对象组成,Element对象负责管理和维护Widget对象在页面上的状态和生命周期。在Element树中,每个Element对象都有一个对应的RenderObject对象来表示它在页面上的可视部分。
-
RenderObject树是页面的渲染树,它由一组RenderObject对象组成,RenderObject对象负责将自己的绘制信息提交给底层的图形引擎进行绘制。在RenderObject树中,每个RenderObject对象都有一个对应的Element对象来表示它在Element树中的关系。
在页面创建渲染过程中,这三棵树是按照一定的规则逐级构建的:
首先,Flutter会根据Widget树中的Widget对象构建对应的Element对象,并把它们按照从上到下、从左到右的顺序连接起来,形成Element树。
接下来,Flutter会根据Element树中的每个Element对象,依次创建对应的RenderObject对象,并把它们按照从上到下、从左到右的顺序连接起来,形成RenderObject树。
最后,Flutter会把RenderObject树提交给底层的图形引擎进行绘制。
在这个过程中,Widget树和Element树是一一对应的,而RenderObject树则是和Element树一一对应的,这样就保证了页面上的每个Widget都能正确地被渲染出来。
网友评论