开始地方

一切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

WidgetBinding

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

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

RootElement