Flutter's setState update principle and process

When calling setState(), you must not have called the dispose() method. Otherwise, an error occurs. You can judge whether calling this method is legal through the mounted attribute.

 

if (mounted) {
  setState(() {});
}

See clearly in the framework In addition to some conditions, the setstate method in dart can be judged as follows:

 

_element.markNeedsBuild();

Let's take a look at markNeedsBuild.

Class needmark needelement

 

  void markNeedsBuild() {
    assert(_debugLifecycleState != _ElementLifecycle.defunct);
    if (!_active)
      return;//return
     ...
    if (dirty)
      return;
    _dirty = true;
    //Call the scheduleBuildFor method
    owner.scheduleBuildFor(this);
  }

Mark the element as "dirty" and add it to the global "dirty" linked list so that it can be updated when the signal is updated in the next frame

  • The "dirty" linked list here is the linked list to be updated. After the update, it will not be "dirty".

  • Because it's a little inefficient to update twice a frame, so_ Return directly when active=false.

Let's take a look at the scheduleBuildFor method that the method finally calls.

BuildOwner class scheduleBuildFor method

The BuildOwner class is the management class of the widget framework, which tracks those widgets that need to be rebuilt.

 

void scheduleBuildFor(Element element) {
     ...
    if (element._inDirtyList) {
      ...
      _dirtyElementsNeedsResorting = true;
      return;
    }
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled();//Callback
    }
    _dirtyElements.add(element);//Add element to the dirty element list
    element._inDirtyList = true;
    assert(() {
      if (debugPrintScheduleBuildForStacks)
        debugPrint('...dirty list is now: $_dirtyElements');
      return true;
    }());
  }

Add an element to_ dirtyElements linked list for widgetsbinding DrawFrame can reconstruct element when calling buildScope. onBuildScheduled() is a callback of BuildOwner.

The callback of the instancescheonscheduled is initialized in the instancescheonschedules.

 

mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    // here
    buildOwner.onBuildScheduled = _handleBuildScheduled;
    window.onLocaleChanged = handleLocaleChanged;window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
 SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);  FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
  }
}

We can see buildowner The onbuildscheduled callback is equal to_ handleBuildScheduled, let's take a look at this now_ handleBuildScheduled method:

 

void _handleBuildScheduled() {
    //Call ensurevisuanupdate
    ensureVisualUpdate();
  }

You can see that the ensurevisuanupdate method is called, so let's go on.

SchedulerBinding class ensurevisuanupdate method

 

  void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        //When schedulerPhase is schedulerPhase idle,
        //SchedulerPhase. Called when postframecallbacks
        //scheduleFrame()
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

The five enumeration values of SchedulerPhase are respectively:

statemeaning
idleThere is no frame being processed. It may be widgetsbinding Scheduletask, scheduleMicrotask, Timer, event handlers, or other callbacks
transientCallbacksSchedulerBinding. The handlebeginframe procedure handles animation status updates
midFrameMicrotasksProcess Microtasks triggered by the transient callbacks phase
persistentCallbacksWidgetsBinding.drawFrame and schedulerbinding Handledrawframe process, build/layout/paint pipeline work
postFrameCallbacksIt is mainly to clean up and plan to execute the work of the next frame

The second case calls the scheduleFrame() method

Let's look at the scheduleFrame() method

 

  void scheduleFrame() {
  if (_hasScheduledFrame || !_framesEnabled) return;
  assert(() {
    if (debugPrintScheduleFrameStacks)
      debugPrintStack(
          label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
    return true;
  }());
  //Calling the scheduleFrame method of Window is a native method
  window.scheduleFrame();
  _hasScheduledFrame = true;
}

WidgetsFlutterBinding is mixed with these bindings, which basically listen to and process some events of Window objects, and then wrap, abstract and distribute these events according to the model of the Framework. You can see that WidgetsFlutterBinding is the "glue" between the Flutter engine and the upper Framework.

nameexplain
GestureBindingProvides window The onpointerdatapacket callback is bound to the Framework gesture subsystem and is the binding entry between the Framework event model and the underlying events
ServicesBindingProvides window Onplatformmessage callback, which is used to bind the platform message channel and mainly handles native and fluent communication
SchedulerBindingProvides window Onbeginframe and window Ondrawframe callback, listen to refresh events, bind Framework drawing and scheduling subsystem
PaintingBindingBinding drawing library, which is mainly used to process picture caching
SemanticsBindingThe bridge between semantic layer and fluent engine is mainly the bottom support of auxiliary functions
RendererBindingProvides window onMetricsChanged ,window.onTextScaleFactorChanged and other callbacks. It is the bridge between rendering tree and fluent engine
WidgetsBindingProvides window Onlocalechanged, onBuildScheduled and other callbacks. It is the bridge between the fluent widget layer and engine

As mentioned in the previous article, the rendering logic of UI is implemented in the Render tree, so let's take a closer look at the logic of renderebinding.

RendererBinding

 

void initInstances() {
  ...

  //Listen for events of Window object
  ui.window
    ..onMetricsChanged = handleMetricsChanged
    ..onTextScaleFactorChanged = handleTextScaleFactorChanged
    ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
    ..onSemanticsAction = _handleSemanticsAction;

  //Add PersistentFrameCallback
  addPersistentFrameCallback(_handlePersistentFrameCallback);
}

addPersistentFrameCallback_ Handlepersistent framecallback finally calls drawFrame, and WidgetsBinding overrides the drawFrame() method in RendererBinding. Finally, we found that we returned to the WidgetsBinding class. The implementation of drawFrame in WidgetsBinding is as follows:

 

@override
void drawFrame() {
 ...
  try {
    if (renderViewElement != null)
      // Refactoring element s that need to be updated
      buildOwner.buildScope(renderViewElement);
    super.drawFrame(); //Call the drawFrame() method of RendererBinding
    buildOwner.finalizeTree();
  }
}

In the above scheduleBuildFor method introduction, it is mentioned: "scheduleBuildFor adds a element to the _dirtyElements list, so that element can be reconstructed when buildScope is invoked in [WidgetsBinding.drawFrame]. onBuildScheduled() is a BuildOwner callback". Calling buildOwner. in drawFrame Buildscope (renderviveelement) updates elements.

 

  void buildScope(Element context, [ VoidCallback callback ]) {
    ...
      while (index < dirtyCount) {
        assert(_dirtyElements[index] != null);
        assert(_dirtyElements[index]._inDirtyList);
        assert(!_dirtyElements[index]._active || _dirtyElements[index]._debugIsInScope(context));
        try {
          //while loop for element refactoring
          _dirtyElements[index].rebuild();
        } catch (e, stack) {
        ...
        }
      }
  }

obtain

Conditional judgment

  • 1. Life cycle judgment

  • 2. Is mounted installed

Management class

  • 1. Tell the management method that it needs to be rebuilt

  • That is, the buildouwner class scheduleBuildFor method

Add dirty linked list

  • "Dirty" linked list is a linked list to be updated

  • 2. After the update, it will not be "dirty"

  • 3._ Return directly when active = false

Call window scheduleFrame()

  • native method

  • Package, abstract and distribute according to the model of the Framework

  • WidgetsFlutterBinding is the "glue" between the Flutter engine and the upper Framework

  • The drawing logic of UI is implemented in the Render tree

Update the frame signal to refresh the interface to be reconstructed

  • "scheduleBuildFor is to add an element to the _dirtyelementslinked list

  • It is easy to reconstruct element when calling buildScope in [WidgetsBinding.drawFrame].

  • onBuildScheduled() is a callback of BuildOwner“

  • Calling buildOwner. in drawFrame Buildscope (renderviveelement) update elements

Figure:

Posted by Rokboy on Wed, 13 Apr 2022 22:00:28 +0930