I’m finally on vacation, I don’t have to write business code all the time, and I finally have some time to tidy up my notes. I’m here to thank you for your early years, I wish you a happy New Year, good luck and good health!

Study notes on Flutter series

  • Flutter Notes — What happened to runApp
  • Flutter Notes — what happens to state.setState
  • Verification code with Dart ID number (available for flutter project)
  • Hellodart-constructor feature
  • Hellodart-mixin, local dialect record multiple inheritance mechanism
  • Flutter notes — MethodChannel(Native&Flutter data interaction)
  • Flutter notes – FlutterActivity

main

The FlutterFramework entry to the Flutter layer is the main function. The default generated function is as follows

void main() {
  runApp(MyApp());
}
Copy the code

1 runApp

RunApp is a top-level function that accepts a Widget as a rootWidget. What happened?

Images, source code, and text parsing are provided below. Because the content is long, it needs to be divided into several parts to learn, and the sequence diagram, record points and actual running order are different. The sorting below is mainly to spread out the knowledge points in the process of runApp from shallow to deep to help better understanding.

2.1 a process

  1. mainFunction, callrunApp(Widget)function
    RunApp, like main, is a top-level function
    void main() {
      runApp(MyApp());
    }
    Copy the code
  2. Initialize WidgetsFlutterBinding. The instance
    voidrunApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() .. scheduleAttachRootWidget(app) .. scheduleWarmUpFrame(); }Copy the code
  3. ensureInitializedThe function creates oneWidgetsFlutterBindingThe singletonWidgetsBinding.instance
    class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {/// verify that WidgetsBinding. The flutter framework works to check whether static WidgetsBinding is completeensureInitialized() {
        if (WidgetsBinding.instance == null)
          WidgetsFlutterBinding();
        returnWidgetsBinding.instance; }}Copy the code
  4. WidgetsFlutterBindingSeven are inherited through the mixIn syntaxBindingBaseSubclass, complete the system function initialization of the whole FlutterFramework level. Refer to this article for mixIn syntaxHellodart-mixin, local dialect record multiple inheritance mechanism.
    abstract class BindingBase {
      
      // Each of the WidgetsFlutterBinding subclasses is called in the WidgetsFlutterBinding integration order
      ///initInstances and initServiceExtensions initialize functions.
      BindingBase() {
        developer.Timeline.startSync('Framework initialization');
    
        assert(! _debugInitialized); initInstances();assert(_debugInitialized);
    
        assert(! _debugServiceExtensionsRegistered); initServiceExtensions();assert(_debugServiceExtensionsRegistered);
    
        developer.postEvent('Flutter.FrameworkInitialization', <String.String>{});
    
        developer.Timeline.finishSync();
      }
    }
    Copy the code
  5. initInstancesandinitServiceExtensionsThis part is a little bit more complicated, so I’m not going to study it here.Just a quick introductionBindingBaseSeries subclass function, the following content I also see notes, if there is an error please point out
    • WidgetsFlutterBinding: Binds FlutterFramework to FlutterEngine
    • GestureBinding: Bind gesture system.
    • ServicesBinding: Main functions anddefaultBinaryMessengerRelating, used in relation to native communication.
    • SchedulerBindingThe: class also inherits ServicesBinding, which is used to schedule frame rendering events.
    • PaintingBinding: Binds to the Painting library
    • SemanticsBinding: Binds the semantic layer to FlutterEngine.
    • RendererBinding: Binds the render tree to FlutterEngine.
    • WidgetsBinding: Binds the Widget layer to FlutterEngine.Once again, the above content is only from the notes, if wrong, thank you for pointing out

Initialize the WidgetsFlutterBinding. The instance and a pile of system after Binding, began the next steps.

voidrunApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() .. scheduleAttachRootWidget(app) .. scheduleWarmUpFrame(); }Copy the code

The scheduleAttachRootWidget(Widget) and scheduleWarmUpFrame functions are invoked using the cascade method, as described below

2.2 process 2

  1. scheduleAttachRootWidget(app): through the way of cascade, generates WidgetsFlutterBinding. Instance instance and static initialization for system function after callWidgetsFlutterBinding.scheduleAttachRootWidget(Widget root)Function.
    voidrunApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() .. scheduleAttachRootWidget(app) .. scheduleWarmUpFrame(); }Copy the code
  2. attachRootWidget(Widget): this is called asynchronouslyWidgetsBinding.scheduleWarmUpFrame(Widget rootWidget)Function to display the UI screen of the Flutter project as quickly as possible.
      void scheduleAttachRootWidget(Widget rootWidget) {
        Timer.run(() {
          attachRootWidget(rootWidget);
        });
      }
    Copy the code
  3. scheduleWarmUpFrame(): In the second point above, asynchronous invocationattachRootWidgetFunction, just to call it as quickly as possible to display a frame. The detailed flow of this function is listed below and will be studied later.
      /// Schedule a frame to run as soon as possible, rather than waiting for
      /// the engine to request a frame in response to a system "Vsync" signal.
      void scheduleWarmUpFrame() {
        ...
      }
    Copy the code
  4. RenderObjectToWidgetAdapterSecond, attachRootWidget binds the Widget, Element, and RenderObject to the BuildOwner object. The BuildOwner reference is the only BuildOwner object that holds the Widget and is passed on through inheritance. Look at the code
    / / / pseudo code
    class WidgetsBinding.{
      void attachRootWidget(Widget rootWidget) {
        / / create a RenderObjectToWidgetAdapter object, generic type T is RenderBox type,
        _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
          container: renderView,
          debugShortDescription: '[root]'.//renderView[note 1] is a render tree object connected to the physical device output layer
          child: rootWidget,  / / root tree Widget).attachToRenderTree(buildOwner, renderViewElement); }}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,
        this.container,
        this.debugShortDescription,
      }) : super(key: GlobalObjectKey(container));
    
      /// The widget below this widget in the tree.
      final Widget child;
    
      /// The [RenderObject] that is the parent of the [Element] created by this widget.
      final RenderObjectWithChildMixin<T> container;
    
      @override
      RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
    
      @override
      RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
    
      @override
      void updateRenderObject(BuildContext context, RenderObject renderObject) { }
    
      ///
      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);
          });
          // 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();
        }
        returnelement; }}Copy the code
  5. attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]): ** It’s aboutelementIgnore the creation of, and learn in Step 3. ** This part of the source code in the fourth, we can seeRenderObjectToWidgetAdapterWill create aRenderObjectToWidgetElementObject, asWidgetsBindingIn the_renderViewElementObject. the_renderViewElementObject is the counterpart of the entire Element tree, which holds the root Widget, the root RenderView, and the BuildOwner object.

2.3 process three

Three main analysis RenderObjectToWidgetAdapter. AttachToRenderTree (BuildOwner owner, [RenderObjectToWidgetElement < T > element]) function, There is also some BaseBinding content mixed in, which is more complicated.

  • BuildOnwer.lockState(fn):

      /// in this function_debugStateLocked Specifies a Boolean value each time the function is calledAll debugStateLockLevel +1,
      If setState is called during _debugStateLocked equals true, an exception will be thrown.
      void lockState(void callback()) {
        assert(callback ! =null);
        assert(_debugStateLockLevel >= 0);
        assert(() {
          _debugStateLockLevel += 1;
          return true; } ());try {
          callback();
        } finally {
          assert(() {
            _debugStateLockLevel -= 1;
            return true; } ()); }assert(_debugStateLockLevel >= 0);
      }
    Copy the code
  • RenderObjectToWidgetAdapter. The createElement method () : it will create a RenderObjectToWidgetElement object.


    I’m not going to look at the properties and operations in its many parent classes

  • Element.assignowner (owner) : Assigns owner:BuildOnwer to Element. This owner:BuildOnwer is the unified manager of the entire Element tree.

  • Element. The mount (null, null) : BuildOwner. BuildScope (Element Context, [VoidCallback callback]), which adds an Element to the build domain, is ignored. And calls the VoidCallback function as a callback. Here we focus on the mount function in the Element object’s parent classes

      owner.buildScope(element, () {
        element.mount(null.null);
      });
    class RenderObjectToWidgetElement.{
      @override
      void mount(Element parent, dynamic newSlot) {
        assert(parent == null);
        super.mount(parent, newSlot);
        // Child is null for now, ignore the rebuild function_rebuild(); }}abstract class RootRenderObjectElement extends RenderObjectElement {
    
      @override
      void mount(Element parent, dynamic newSlot) {
        super.mount(parent, newSlot); }}RenderObjectElement _renderObject is the most important attribute in the class
    /// Is the RenderObject object of the root Element, which stores the RenderObject class
    /// Skia is a 2D rendering framework based on the Cartesian axis, with Element location information
    RenderObject also implements basic rendering functions.
    abstract class RenderObjectElement extends Element {
      /// The underlying [RenderObject] for this element.
      @override
      RenderObject get renderObject => _renderObject;
      RenderObject _renderObject;
      @override
      void mount(Element parent, dynamic newSlot) {
        super.mount(parent, newSlot);
        / / in the back to step 2 RenderObjectToWidgetAdapter class, a PipelineOwner RendererBinding class after initialization
        // The PipelineOwner object has a rootNode object, which is called _renderObject
        _renderObject = widget.createRenderObject(this);
        // newSlot equals null, which is ignored
        attachRenderObject(newSlot);
        // Set dirty to false,
        _dirty = false; }}class Element.{
      @mustCallSuper
      void mount(Element parent, dynamic newSlot) {
        // An assignment_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;
        if (widget.key is GlobalKey) {
          final GlobalKey key = widget.key;
          key._register(this);
        }
        _updateInheritance();
        assert(() {
          _debugLifecycleState = _ElementLifecycle.active;
          return true;
        }());
      }    
    }
    Copy the code

    The mount(Element parent, dynamic newSlot) function is used to mount an Element to the Element tree. If the parent property is present, the value is assigned and the state of the Element changes to active.

  • SchedulerBinding. Instance. EnsureVisualUpdate () : End the function call WidgetsFlutterBinding. HandleDrawFrame () function, this part of knowledge in the Flutter notes – runApp what happened (source) article analysis study.

      void ensureVisualUpdate() {
        switch (schedulerPhase) {
          case SchedulerPhase.idle:
          case SchedulerPhase.postFrameCallbacks:
            scheduleFrame();
            return;
          case SchedulerPhase.transientCallbacks:
          case SchedulerPhase.midFrameMicrotasks:
          case SchedulerPhase.persistentCallbacks:
            return; }}Copy the code
  • SchedulerBinding. ScheduleFrame () : then is called the window. The scheduleFrame () function, which is a native function, pure FlutterFramework partial clues is broken.

      void scheduleFrame() {
        if(_hasScheduledFrame || ! _framesEnabled)return;
        assert(() {
          if (debugPrintScheduleFrameStacks)
            debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
          return true; } ()); ensureFrameCallbacksRegistered();window.scheduleFrame();
        _hasScheduledFrame = true;
      }
    Copy the code

3 summary

Here is a simple process diagram of runApp that hides many of the details

  1. Dart part of the FlutterFramework, the program entry ismainFunction.
  2. callrunApp(Widget)The function passes in a Widget as the root Widget.
  3. WidgetJust a configuration class, not an actual UI element.
  4. runAppthroughWidgetsFlutterBindingMixins are initialized from a set of parent classes.
  5. Among them,RendererBindingIn the parent classrenderViewObject is the actual render object.
  6. throughRenderObjectToWidgetAdapterClass to generate oneRenderObjectToWidgetElement<RenderBox>Type Element as the root Element, and let widgets, renderView, and BuildOwner relate to the root Element.
  7. Call to mount the root ElementSchedulerBinding.instance.ensureVisualUpdate()Function to wait for the next frame to render.
  8. scheduleAttachRootWidgetIs a time-consuming operation that runs asynchronously. RunApp takes precedencescheduleWarmUpFrame()Render preheat frames.

The above article is a summary of the author’s study of the source code of Flutter1.12. If there are any mistakes or omissions, please kindly point out and correct them. Thank you very much.

In addition, this article only traces the source code of runApp. How does Flutter render to Native devices and the effects of BaseBinding subclasses need to be further studied, while rendering callsWidgetsFlutterBinding.handleDrawFrame()Part of the function knowledge is inFlutter Notes — What happened to runAppThe article analyzes learning ha.