1 introduction
With Flutter, multi-layer widgets are common due to its radical composition (Everything is Widget) design philosophy, and how to transfer data between widgets in different layers is a common problem for developers. It is common to add data to the constructor of the lower-level Widget. But if the Widget tiers that pass data are far apart, it can be a nightmare to pass data down from one layer to the next. Therefore, Flutter provides a way to pass data from top to bottom in the Widget tree: InheritedWidget. This article will go into more detail on how inheritedWidgets work and illustrate how to use them.
2 core function
- updateShouldNotify
UpdateShouldNotify controls whether components that depend on the InheritedWidget need to be rebuilt. If updateShouldNotify is true, then widgets that depend on InheritedWidget will be rebuilt whenever there is a change in InheritedWidget, Its Element’s didChangeDependencies function is called to update the display in the child Widget. Otherwise, widgets that depend on the InheritedWidget are not rebuilt.
The flowchart is as follows:
You can see that when the InheritedWidget changes, if updateShouldNotify is true, it calls the child’s didChangeDependencies function via notifyClients, which calls markNeedsBuild, Add this Element to the _dirtyElements list. As you all know, _dirtyElements holds the Element that needs to be rebuilt and will be rebuilt in the next frame, so the child components will be rebuilt in the next frame.
InheritedElement’s updated source is
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
Copy the code
Element’s didChangeDependencies source is as follows:
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
Copy the code
3 Core process
- Storage InheritedWidget
Given that InheritedWidget can be used to allow users to quickly get it from a child component, the inevitable question is how is it passed to the child component? The Flutter Framework also passes down the InheritedWidget layer by layer, but the process is transparent to us because the Framework layer handles it itself. Let’s now go through the process of passing inheritedWidgets.
Within Element, there is a map of _inheritedWidgets. All inheritedElements in the parent node are saved. The source code is as follows:
Map<Type, InheritedElement> _inheritedWidgets;
Copy the code
In key, Type is a subclass of InheritedWidget and value is an InheritedElement. Why is the value saved as an InheritedElement instead of an InheritedWidget? As you know from the previous article, an Element holds a reference to the corresponding Widget, so you can get the corresponding InheritedWidget from an InheritedElement. Also, widgets are rebuilt when the parent Widget is rebuilt, so it’s more appropriate to save inheritedElements. In a normal Element, _inheritedWidgets directly copies the values of _inheritedWidgets in its parent component. The source code is as follows:
void _updateInheritance() {
assert(_active); _inheritedWidgets = _parent? ._inheritedWidgets; }Copy the code
In the InheritedElement, _inheritedWidgets first copies the value of _inheritedWidgets in its parent and then adds itself to the list. The source code is as follows:
@override
void _updateInheritance() {
assert(_active);
final Map<Type, InheritedElement> incomingWidgets = _parent? ._inheritedWidgets;if(incomingWidgets ! =null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets[widget.runtimeType] = this;
}
Copy the code
As you can see, inheritedElements are passed down one layer at a time. _inheritedWidgets assignment process
As you can see from the flowchart, the _inheritedWidgets are assigned to the Element when it is added to the Element Tree and are therefore accessible in the build function of the child component.
- Get InheritedWidget
Now that we know that inheritedElements are passed to the child components, how do we get it? Flutter provided specifically for a certain InheritedWidget dependOnInheritedWidgetOfExactType type of function. The source code is as follows:
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
if(ancestor ! =null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
Copy the code
As you can see from the third line, this function looks for an InheritedElement from _inheritedWidgets and returns its InheritedWidget. Besides dependOnInheritedWidgetOfExactType, Flutter also provides another special for a InheritedWidget type function: getElementForInheritedWidgetOfExactType. The source code is as follows:
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
Copy the code
Compared with the dependOnInheritedWidgetOfExactType source code, you can see more dependOnInheritedWidgetOfExactType dependOnInheritedElement function call, This function is used to create InheritedWidget and invoke dependOnInheritedWidgetOfExactType component dependencies. It has two steps:
- Will rely on theInheritedElementTo join thisElementIn the _dependencies list, which contains the dependencies listElementAll dependentInheritedElement.
- Will thisElementdependentInheritedElementThe _dependents map for which all dependencies are storedInheritedElementtheElement.
If you are using dependOnInheritedWidgetOfExactType, when being dependent on InheritedWidget is updated, dependent child components will be rebuild; And when using getElementForInheritedWidgetOfExactType, not being able to set up a corresponding dependencies InheritedWidget is updated, dependent child components will not be rebuild.
4 sample
Here is an example of how to use it.
First, we save the current count of counters in the Data property of ShareDataWidget by InheritedWidget:
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data; // The data that needs to be shared in the subtree saves the click count
// Define a convenient method for widgets in the subtree to get shared data
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
// This callback determines whether to notify widgets in the subtree that depend on data when the data changes
@override
bool updateShouldNotify(ShareDataWidget old) {
// If true is returned, the subtree depends on the widget
/ / the child widgets ` state. DidChangeDependencies ` will be invoked
return old.data != data;
}
}
Copy the code
We then implement a child component, _TestWidget, that references the data in ShareDataWidget in its build method. In the meantime, print the log in its didChangeDependencies callback:
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
print("__TestWidgetState build");
// Use InheritedWidget to share data
return Text(ShareDataWidget
.of(context)
.data
.toString());
// return Text("tex");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
UpdateShouldNotify is called when an InheritedWidget in the parent or ancestor widget changes (updateShouldNotify returns true)
// This callback is not called if the build does not rely on InheritedWidget.
print("Dependencies change"); }}Copy the code
Finally, we create a button that increments the value of ShareDataWidget with each click:
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget( / / use ShareDataWidget
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_TestWidget(),
RaisedButton(
child: Text("Increment"),
// With each click, incrementing the count and re-build, the data of ShareDataWidget will be updatedonPressed: () => setState(() => ++count), ) ], ), ), ); }}Copy the code
5 subtotal
This article introduces the core functions and processes of an InheritedWidget and shows an example of an InheritedWidget. The highlights are as follows:
- InheritedWidgetthroughupdateShouldNotifyFunction controls whether dependent child components are inInheritedWidgetChanges will be reconstructed: ifupdateShouldNotifyReturns true,InheritedWidgetChanges when the child componentbuildWill be called, and vice versa.
- InheritedWidgetIs stored in theinheritedWidgetsIs passed through layers into child components.
- Can pass in the child componentdependOnInheritedWidgetOfExactTypeorgetElementForInheritedWidgetOfExactTypeObtain the correspondingInheritedWidgetObject in whichdependOnInheritedWidgetOfExactTypeIt creates dependencies, soInheritedWidgetChanges when the child componentbuildWill be called,getElementForInheritedWidgetOfExactTypeYou don’t build dependencies,InheritedWidgetChanges when the child componentbuildWill not be called.
6 Reference Documents
Flutter of actual combat
7 Related Articles
Framework Analysis of Flutter (1) — Framework Analysis of Flutter (2) — Framework Analysis of Widget Flutter (3) — Framework Analysis of Element Flutter (4) — Framework Analysis of RenderObject Flutter (5) — Widget, Element, RenderObject Tree Flutter framework analysis (6) -Constraint Flutter framework Analysis (7) -relayoutBoundary Flutter Framework Analysis (8) -Platform Channel Flutter framework Analysis – Parent Data