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.
setState
How 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:
Element
Triggering 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.
- will
InheritedWidget
Wrapped inStatefulWidget
Inside, the implementation ofsetState
. The triggerstateful
Component internal methods:ComponentElement
->performRebuild
->updateChild
->child.update
. InheritedElement
Is fired as a child nodeupdate
Methods.
// 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
updateShouldNotify
Method is used forInheritedWidget
Subclass implementation, only returntrue
Is triggered by the child componentdidChangeDependencies
Methods. (if there is a dependency),didChangeDependencies
Will be calledmarkNeedsBuild
.