一切Flutter app启动的时候都会调用runApp方法。
首先初始化WidgetsBinding,然后调用_runWidget方法。
void runApp(Widget app) {
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
_runWidget(binding.wrapWithDefaultView(app), binding, 'runApp');
}
void _runWidget(Widget app, WidgetsBinding binding, String debugEntryPoint) {
assert(binding.debugCheckZone(debugEntryPoint));
binding
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
sequenceDiagram
_runWidget->>+WidgetsBinding: scheduleAttachRootWidget
WidgetsBinding-->- _runWidget:
_runWidget->>+WidgetsBinding: scheduleWarmUpFrame
scheduleAttachRootWidget → attachRootWidget → attachToBuildOwner。
attachRootWidget将创建RootWidget,并将这个对象作为参数传递给attachToBuildOwner方法,
attachToBuildOwner 方法拿到这个参数后会调用该Widget的attach方法。
flowchart TB
C --> D
C --> E{Is Boostrap Frame}
E -- Yes --> F
subgraph WidgetBinding
A[scheduleAttachRootWidget] -->B[attachRootWidget]
B --> C[attachToBuildOwner]
end
subgraph RootWidget
D[widget.attach]
end
subgraph ScheduleBinding
F[instance.ensureVisualUpdate]
end
/// Schedules a [Timer] for attaching the root widget.
///
/// This is called by [runApp] to configure the widget tree. Consider using
/// [attachRootWidget] if you want to build the widget tree synchronously.
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
/// Takes a widget and attaches it to the [rootElement], creating it if
/// necessary.
///
/// This is called by [runApp] to configure the widget tree.
///
/// See also:
///
/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
/// widget and attaches it to the render tree.
void attachRootWidget(Widget rootWidget) {
attachToBuildOwner(RootWidget(
debugShortDescription: '[root]',
child: rootWidget,
));
}
/// Called by [attachRootWidget] to attach the provided [RootWidget] to the
/// [buildOwner].
///
/// This creates the [rootElement], if necessary, or re-uses an existing one.
///
/// This method is rarely called directly, but it can be useful in tests to
/// restore the element tree to a previous version by providing the
/// [RootWidget] of that version (see [WidgetTester.restartAndRestore] for an
/// exemplary use case).
void attachToBuildOwner(RootWidget widget) {
final bool isBootstrapFrame = rootElement == null;
_readyToProduceFrames = true;
_rootElement = widget.attach(buildOwner!, rootElement as RootElement?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
RootWidget是Widget的根结点。
attach方法会会创建RootElement,然后给该Element对象的BuildOwner绘制,使其拥有触发Widget重绘的能力。并调用Element的mount方法来完成Widget的装载。
/// Inflate this widget and attaches it to the provided [BuildOwner].
///
/// If `element` is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [WidgetsBinding.attachToBuildOwner] (which is indirectly called by
/// [runApp]) to bootstrap applications.
RootElement attach(BuildOwner owner, [ RootElement? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
element!.mount(/* parent */ null, /* slot */ null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
---
title: RootWidget attach method flow chart
---
flowchart TB
A{element is null}
A --> |Yes|B
A --> |No|E
B[createElement] --> C(assignOwner)
C --> D[buildScope]
D -->|run in next time event| mount
E[_newWidget = this]
E --> F[markNeedsBuild]
subgraph RootElement
mount
end