Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Element’s refresh mechanism

We know that the entire view layer of flutter is a tree structure that is laid out in the form of parent and child nodes. When we first touch flutter, we use setState to refresh the page. This refresh method is called full refresh. When the parent node is refreshed, all the children under the parent node will execute the build method to refresh.

setStateHow is refresh implemented?

SetState is refreshed through element.markNeedsBuild

// Code in State
@protected
voidsetState(VoidCallback fn) { ... Omit _element! .markNeedsBuild(); }// The code in Element
voidmarkNeedsBuild() { ... Omit _dirty =true; owner! .scheduleBuildFor(this);
}
Copy the code

Parent node refresh How can I trigger child node refresh?

First, we need to know what Element does when it performs a refresh:

ElementTriggering a refresh performs three important methodsreBuild -> performRebuild -> build

Combined with the source code, can see that the commonly used as StatefulElement StatelessElement, ProxyElement inherit from ComponentElement, etc. Let’s take a look at how these three methods are implemented at ComponentElement.

/ / from the Element
@pragma('vm:prefer-inline')
voidrebuild() { ... Omit performRebuild (); }/ / from ComponentElement
@override
@pragma('vm:notify-debugger-on-exception')
voidperformRebuild() { ... Widget? built; built = build(); . updateChild(_child, built, slot); }/ / from ComponentElement
@protected
Widget build();
Copy the code

ComponenetElement overrides the performRebuild method, executing both build and updateChild. When the configuration is updated (child.widget! = newWidget), and updateChild calls the child.update(newWidget) method.

/ / from ComponentElement
@protected
Element updateChild(Element child, Widget newWidget, dynamicnewSlot) { ... omitif(hasSameSuperclass && child.widget == newWidget) { ... Omit}else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
      child.update(newWidget); 
    }
  return newChild;
}
Copy the code

Note: The prerequisite for executing child.update is child.widget! = newWidget

Inherited Local refresh mechanism

If we use InheritedWidget with setState, only the state-dependent components in the InheritedWidget are rebuilt, and the rest of the non-dependent components are not rebuilt.

1. Kill full refresh

From the source analysis, InheritedElement inherited from ProxyElement, ProxyElement inherited from ComponentElement.

  • willInheritedWidgetWrapped inStatefulWidgetInside, the implementation ofsetState. The triggerstatefulComponent internal methods:ComponentElement -> performRebuild -> updateChild -> child.update.
  • InheritedElementIs fired as a child nodeupdateMethods.
// Proxy overwrites the update method
@override
void update(ProxyWidget newWidget) {
  ...
  updated(oldWidget);
  _dirty = true;
  rebuild();
}
Copy the code

In the update method, the rebuild method is executed. From the above we know that the rebuild method will eventually execute updateChild to refresh the child node. InheritedElement and ProxyElement do not override the performRebuild method. How does Interited block the high-cost strategy of full flush?

In ProxyElement versus StatefulElement, the build method does not call the build method of the corresponding Widget object, but instead returns widget.child.

// Build method of ProxyElement
@override
Widget build() => widget.child;

// StatefulElement build method
@override
Widget build() => state.build(this);
Copy the code

Combined with the child.update trigger condition analyzed above, since the build method was not rebuilt, child.widget! = newWidget is not valid. So the subcomponent tree is not rebuilt and the child.update method is not triggered

2. Associated refresh

Inherited Notifies Dependent Components to refresh. Let’s take a closer look at the internal implementation of ProxyElement:

abstract class ProxyElement extends ComponentElement {

  @override
  Widget build() => widget.child;

  @override
  voidupdate(ProxyWidget newWidget) { ... Omit the updated (oldWidget); _dirty =true;
    rebuild();
  }

  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }

  @protected
  void notifyClients(covariant ProxyWidget oldWidget);
}
Copy the code

Update -> Updated -> notifyClient

NotifyClienet is implemented in an InteritedElement, updating all dependent subcomponent trees

/ / from InteritedElement
@override
void notifyClients(InheritedWidget oldWidget) {
  for (final Element dependent in_dependents.keys) { ... Omit notifyDependent (oldWidget, dependent); }}/ / from InteritedElement
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
  dependent.didChangeDependencies();
}

/ / from the Element
@mustCallSuper
void didChangeDependencies() {
  markNeedsBuild();
}
Copy the code

A _dependents Element is a HashMap

maintained by InteritedElement for all elements that are dependent on it. Through the context. DependOnInheritedWidgetOfExactType binding dependencies.
,>

supplement

  • updateShouldNotifyMethod is used forInheritedWidgetSubclass implementation, only returntrueIs triggered by the child componentdidChangeDependenciesMethods. (if there is a dependency),didChangeDependenciesWill be calledmarkNeedsBuild.