A: an overview

The source code of this article is copied from Stable 1.17.5, and the non-core code has been deleted

I flipped through the sequence of flutter source calls and drew a simple flow chart

Two: start flowchart

Three: Start the detailed process

3.1 the main

void main() => runApp(MyApp());
Copy the code

3.2 runApp

[flutter/lib/src/widgets/binding.dart]

/// Fill the incoming widget full screen
///
/// Incoming widgets are forced to fill the screen. If you want to adjust the alignment of widgets, consider [Align], [Center], and so on
///
/// Calling [runApp] again will replace the root widget from the previous screen with the one passed in.
/// The new widget tree is compared to the previous widget tree, and any differences are applied to the underlying render tree
/// Similar to [StatefulWidget] rebuilt after calling [state.setState]
///
/// If necessary, initialize the binding with [WidgetsFlutterBinding].
///
/// See also:
///
///  *[WidgetsBinding attachRootWidget], create the root widget widget structure
///  *[RenderObjectToWidgetAdapter attachToRenderTree], element structure to create the root element.
///  * [WidgetsBinding.handleBeginFrame], Function callback of _transientCallbacks
/// To ensure that the widgets, elements, and render tree are built.
voidrunApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() .. scheduleAttachRootWidget(app) .. scheduleWarmUpFrame(); }Copy the code

As you can roughly infer from this, runApp does three things:

  • Initialize WidgetsFlutterBinding
  • Load the interface App into the Flutter tree
  • Calculate the area and start drawing

3.3 the ensureInitialized

[flutter/lib/src/widgets/binding.dart]

/// Make a function set binding for the program
///
/// Glue that binds the framework to the engine layer
class WidgetsFlutterBinding extends BindingBase with GestureBinding.ServicesBinding.SchedulerBinding.PaintingBinding.SemanticsBinding.RendererBinding.WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    returnWidgetsBinding.instance; }}Copy the code

This is a set of features (flutter uses the mixin syntax to achieve multiple inheritance effects) that include:

  • GestureBinding: provides a window onPointerDataPacket callback, binding Framework gestures subsystem, is the binding Framework model and the underlying events entrance.
  • ServicesBinding: Provides window.onPlatformMessage callback for binding platform message channels, which handle native and Flutter communication.
  • SchedulerBinding: Provides window.onBeginFrame and Window. onDrawFrame callbacks, listens for refresh events, and binds the Framework to draw scheduling subsystems.
  • PaintingBinding: Binds the drawing library to handle image caching.
  • SemanticsBinding: A bridge between the semantic layer and the Flutter engine, mainly providing low-level support for ancillary functions.
  • RendererBinding: provides a window. OnMetricsChanged, window. OnTextScaleFactorChanged callback, etc. It is the bridge between the render tree and the Flutter engine.
  • WidgetsBinding: Provides callbacks to window.onLocaleChanged, onBuildScheduled, etc. It is the bridge between the Flutter Widget layer and the Engine.

3.4 scheduleAttachRootWidget

[flutter/lib/src/widgets/binding.dart]

/// Generate a timer and attach it to the root widget.
///
/// If you want to build the widget tree synchronously, use [attachRootWidget]
void scheduleAttachRootWidget(Widget rootWidget) {
  Timer.run(() {
    attachRootWidget(rootWidget);
  });
}
Copy the code

3.4.1 track attachRootWidget

[flutter/lib/src/widgets/binding.dart]

/// Attach the widget to renderViewElement (the root Element of the Element tree)
///
/// [runApp] is configured to generate the Widget tree
///
/// See also: [RenderObjectToWidgetAdapter attachToRenderTree].
void attachRootWidget(Widget rootWidget) {
  / / constructs a RenderObjectToWidgetAdapter instance, it is inherited from the Widget, so its nature is a Widget. The Widget we wrote (the rootWidget variable) is passed in as a child argument, and the Render tree root, RenderView, is passed in as a container argument.
  // This is the root of our Widget tree, where our Widget hangs, and the corresponding RenderObject is the RenderView.

  // _renderViewElement is the root tree of an element, initialized when [runApp] is first called
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement);
  // buildOwner manages the build of the Widget tree
  // Initializing [_buildOwner] must be done in the [initInstances] method,
  // Because it requires [ServicesBinding] to set up the [defaultBinaryMessenger] instance.
}
Copy the code

3.4.2 attachToRenderTree

[flutter/lib/src/widgets/binding.dart]

  /// Fill the screen with the current widget and return element as a subtree of the [Element] tree
  ///
  /// If `element`Is null, which creates an element. else Passing in element will have to replace the widget.
  ///
  /// Used for [runApp] to boot the application
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {   //lockState, disallow the setState method during the following code execution
        element = createElement(); //
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        element.mount(null.null);
      });
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }
Copy the code
3.4.2.1 createElement method
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

  /// Create a node managed by [RenderObject]
  ///
  /// The created [RenderObject] is not added to the Render tree and is called
  ///Add [RenderObjectToWidgetAdapter attachToRenderTree]
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);
Copy the code
3.4.2.2 buildScope

[flutter/lib/src/widgets/framework.dart]

/// Create a scope for the updated widget tree and call the given 'callback '(if any)
/// Then, use [scheduleBuildFor] to build all the elements marked dirty in depth order
///
/// This mechanism prevents a build method from transiently requiring other build methods to run, which can lead to an infinite loop
///
/// The dirty list is processed after 'callback' returns, and all elements marked dirty are built in depth order using [scheduleBuildFor]
/// If an element is marked as dirty when this method runs, it must be deeper than the "context" node and deeper than any previously built node
///

/// To refresh the current dirty list without doing anything else, this function can be called without a callback
/// The framework calls [WidgetsBinding. DrawFrame] for each frame drawn.
///
/// Only one [buildScope] can be activated at a time.
///
/// Each [buildScope] has its own [lockState] scope.
///
/// To print console messages every time this method is called, set [debugPrintBuildScope] to true.
/// This method is useful for debugging problems where widgets are not marked as dirty or are marked as dirty too often.
void buildScope(Element context, [ VoidCallback callback ]) {
    if (callback == null && _dirtyElements.isEmpty)
        return;
    Timeline.startSync('Build', arguments: timelineWhitelistArguments);
    try {
        _scheduledFlushDirtyElements = true;
        if(callback ! =null) {
            Element debugPreviousBuildTarget;
            _dirtyElementsNeedsResorting = false;
            try {
                callback(); / / callback mount ()}... } _dirtyElements.sort(Element._sort);
        _dirtyElementsNeedsResorting = false;
        int dirtyCount = _dirtyElements.length;
        int index = 0;
        while (index < dirtyCount) {
            try {
                _dirtyElements[index].rebuild(); // Rebuild for dirty elements}... }}finally{... Timeline.finishSync(); }}Copy the code
3.4.2.3 mount

[flutter/lib/src/widgets/binding.dart]

@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _rebuild();
}
Copy the code
3.4.2.3.1 mount

[flutter/lib/src/widgets/framework.dart]

/// Adds this element to the given location of the given parent node.
///
/// The framework calls this function the first time a newly created element is added to the tree. Using this method to initialize depends on having a parent class state.
/// State independent of the parent class is easier to initialize in the constructor.
///
/// This method transforms the element from the "Initial" lifecycle state to the "Active" lifecycle state.
///
/// Subclasses that override this method might also override [update], [visitChildren],
/// [RenderObjectElement.insertRenderObjectChild],
/// [RenderObjectElement.moveRenderObjectChild], 
/// [RenderObjectElement.removeRenderObjectChild].
@mustCallSuper
void mount(Element parent, dynamicnewSlot) { _parent = parent; _slot = newSlot; _depth = _parent ! =null ? _parent.depth + 1 : 1;
  _active = true;
  if(parent ! =null) // Only assign ownership if the parent is non-null
    _owner = parent.owner;
  final Key key = widget.key;
  if (key is GlobalKey) {
    key._register(this);
  }
  _updateInheritance();
}
Copy the code
3.4.2.3.2 mount

[flutter/lib/src/widgets/framework.dart]

Override void mount(Element parent, dynamic newSlot) {super.mount(parent, newSlot); _renderObject = widget.createRenderObject(this); AttachRenderObject (newSlot); _dirty = false; }Copy the code
3.4.2.4 _rebuild

[flutter/lib/src/widgets/binding.dart]

void _rebuild() {
  try {
    _child = updateChild(_child, widget.child, _rootChildSlot);
  } catch (exception, stack) {
    final FlutterErrorDetails details = FlutterErrorDetails(
      exception: exception,
      stack: stack,
      library: 'widgets library',
      context: ErrorDescription('attaching to the render tree')); FlutterError.reportError(details);final Widget error = ErrorWidget.builder(details);
    _child = updateChild(null, error, _rootChildSlot); }}Copy the code
3.4.2.5 updateChild

[flutter/lib/src/widgets/framework.dart]

/// Updates the given child node with the given new configuration.
///
/// This method is at the heart of the Widgets system. It is called every time you "add," "update," and "remove child nodes" based on the updated configuration.
///
/// `newSlot`Parameter specifies the [for this element.slot] (Node trough point).
///
/// if`child`Null, and`newWidget`If it is not null, you need to create a new element for it,
/// And configure an [Element] using the newWidget.
///
/// if`newWidget`Null, and`child`If it is not null, it needs to be deleted because it has no configuration information.
///
/// If neither value is null, the configuration of 'child' needs to be updated to the new configuration provided for 'newWidget'.
/// Given "newWidget" if it can be provided to an existing child node (as determined by [widget.canupdate]).
/// Otherwise, you need to discard the old child node and create a new child node for the new configuration.
///
/// If both are null, there are no children and no children, so nothing is done
///
/// The [updateChild] method returns a new child node if one must be created;
/// If a child node must be updated, the child node passed in is returned; If the child node must be removed without replacing it, null is returned.
///
/// The following table summarizes the above:
///
/// |                     | **newWidget == null**  | **newWidget ! = null**   |
/// | : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- : | : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | : -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - |
/// |  **child == null**  |  Returns null.         |  Returns new [Element]. |
/// |  **child ! = null**| delete Old child, returns null. | if possible to update Old child, return to the child or new [Element]. |

@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  if (newWidget == null) {
    if(child ! =null)
      deactivateChild(child);
    return null;
  }
  Element newChild;
  if(child ! =null) {
    bool hasSameSuperclass = true;
    if (hasSameSuperclass && child.widget == newWidget) {
      if(child.slot ! = newSlot) updateSlotForChild(child, newSlot); newChild = child; }else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
      if(child.slot ! = newSlot) updateSlotForChild(child, newSlot); child.update(newWidget); newChild = child; }else{ deactivateChild(child); newChild = inflateWidget(newWidget, newSlot); }}else {
    newChild = inflateWidget(newWidget, newSlot);
  }
  return newChild;
}
Copy the code

3.5. ScheduleWarmUpFrame

[flutter/lib/src/widgets/binding.dart]

/// Draw frames as quickly as possible, rather than waiting for the Vsync signal to respond
///
/// Used when the program starts so that the first frame draws can gain a few extra milliseconds
///
/// Lock event scheduling until this frame is drawn
///
/// If [scheduleFrame] or [scheduleForcedFrame] is executed in advance, it is delayed
///
/// This method does not execute if another frame drawing schedule has already started, or if scheduleWarmUpFrame is called elsewhere
///
/// [scheduleFrame] is preferred for updating attempts
void scheduleWarmUpFrame() {
  if(_warmUpFrame || schedulerPhase ! = SchedulerPhase.idle)return;
  _warmUpFrame = true;
  Timeline.startSync('Warm-up frame');
  final bool hadScheduledFrame = _hasScheduledFrame;
  Timer.run(() {
    handleBeginFrame(null);
  });
  Timer.run(() {
    handleDrawFrame();//
    resetEpoch();// Reset the time
    _warmUpFrame = false;
    if (hadScheduledFrame)
      scheduleFrame();
  });
  // Event locking
  lockEvents(() async {
    await endOfFrame;
    Timeline.finishSync();
  });
}
Copy the code

3.5.1 track of. HandleBeginFrame

[flutter/lib/src/widgets/binding.dart]

/// Called by the engine to prepare the framework to generate a new frame.
///
/// This function calls all TRANSIENT FRAME CALLBACKS registered with [scheduleFrameCallback].
/// Then go back and run any scheduled microtasks (for example, handlers for any [Future] resolved through transient frame callbacks),
/// And call [handleDrawFrame] to continue the frame.
///
/// If the incoming timestamp is null, the timestamp of the last frame is reused.
///
/// If at the beginning of each frame in the debug mode showed cross, will [debugPrintBeginFrameBanner] is set to true.
/// The banner is printed to the console using [debugPrint] and contains the frame number (plus 1 per frame) and the timestamp of the frame.
/// If the passed timestamp is empty, the string "warm-up frame" is displayed instead of the timestamp.
/// It allows a response to the operating system's "Vsync" signal, distinguishing the frames actively pushed in by the frames from those requested by the engine.
///
/// You can also set [debugPrintEndFrameBanner] to true to display the banner at the end of each frame.
/// This allows developers to distinguish whether a log is printed 'during' or 'in between' (for example, in response to an event or timer).
void handleBeginFrame(Duration? rawTimeStamp) {
  Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent); _firstRawTimeStampInEpoch ?? = rawTimeStamp; _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);if(rawTimeStamp ! =null)
    _lastRawTimeStamp = rawTimeStamp;
    _hasScheduledFrame = false;
  try {
    // TRANSIENT FRAME CALLBACKS
    Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
    _schedulerPhase = SchedulerPhase.transientCallbacks;
    final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
    _transientCallbacks = <int, _FrameCallbackEntry>{};
    callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
      if(! _removedIds.contains(id)) _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp! , callbackEntry.debugStack); }); _removedIds.clear(); }finally{ _schedulerPhase = SchedulerPhase.midFrameMicrotasks; }}Copy the code

3.5.2. handleDrawFrame

[flutter/lib/src/widgets/binding.dart]

/// Called by the engine to draw a new frame.
///
/// This method is called immediately after handleBeginFrame.
/// It calls by addPersistentFrameCallback register all of the callback, the callback usually drive rendering pipeline,
/// The callback registered by [addPostFrameCallback] is then invoked.
///
/// See Debugging hooks for handleBeginFrame, which can be useful when dealing with frame callbacks.
void handleDrawFrame() {
  assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
  Timeline.finishSync(); // end the "Animate" phase
  try {
    // PERSISTENT FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.persistentCallbacks;
    for (final FrameCallback callback in_persistentCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp!) ;// POST-FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.postFrameCallbacks;
    final List<FrameCallback> localPostFrameCallbacks =
        List<FrameCallback>.from(_postFrameCallbacks);
    _postFrameCallbacks.clear();
    for (final FrameCallback callback inlocalPostFrameCallbacks) _invokeFrameCallback(callback, _currentFrameTimeStamp!) ; }finally {
    _schedulerPhase = SchedulerPhase.idle;
    Timeline.finishSync(); // end the Frame
    _currentFrameTimeStamp = null; }}Copy the code

3.5.3. scheduleFrame

[flutter/lib/src/widgets/binding.dart]

/// If necessary, a new frame can be scheduled by calling [window.scheduleFrame].
///
/// After calling this method, the engine will (eventually) call [handleBeginFrame].
/// (The call may be delayed, for example, if the screen is closed, usually until the screen is open and the application is visible.)
/// Calling this method in one frame forces another frame to be scheduled, even if the current frame has not yet completed.
///
/// The scheduling frame is triggered by the "Vsync" signal provided by the operating system.
/// Historically, the screen refreshes the display through Vsync signal
/// The hardware is now more complex, but the idea of refreshing the APP by re-rendering it with a "Vsync" signal continues to be used.
///
/// If you want to print scheduling frame stack information, [debugPrintScheduleFrameStacks] must be set to true.
///
/// See also:
///
///  *[scheduleForcedFrame], [lifecycleState] will be ignored when scheduling frames.
///  *[scheduleWarmUpFrame], completely ignoring the "Vsync" signal and immediately triggering the frame drawing.
void scheduleFrame() {
  if(_hasScheduledFrame || ! framesEnabled)return;
  ensureFrameCallbacksRegistered();
  window.scheduleFrame();
  _hasScheduledFrame = true;
}
Copy the code