On the first code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter InheritWidget', home: Scaffold( appBar: AppBar(), body: Center( child: BodyWidget(), ), ), ); }}class BodyWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
returnBodyWidgetState(); }}class BodyWidgetState extends State<BodyWidget> {
int count = 0;
@override
Widget build(BuildContext context) {
print("BodyWidgetState build:$hashCode");
return CustomInheritedWidget(
data: count,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
DependOnInheritedWidget<int>(),
Builder(builder: (context) {
return CustomRaisedButton(
onPressed: () {
setState(() {
count++;
});
},
child: Text("Digital + 1")); })],),); }}class CustomRaisedButton extends RaisedButton {
const CustomRaisedButton({
@required VoidCallback onPressed,
Widget child,
}) : super(onPressed: onPressed, child: child);
@override
Widget build(BuildContext context) {
print("CustomRaisedButton build:$hashCode");
return super.build(context); }}class DependOnInheritedWidget<T> extends StatefulWidget {
DependOnInheritedWidget(){
print("DependOnInheritedWidget:$hashCode");
}
@override
State<StatefulWidget> createState() {
returnDependOnInheritedWidgetState<T>(); }}class DependOnInheritedWidgetState<T> extends State<DependOnInheritedWidget> {
@override
Widget build(BuildContext context) {
print("DependOnInheritedWidgetState build:$hashCode");
return Text(CustomInheritedWidget.of<T>(context).data.toString());
}
@override
void didChangeDependencies() {
print("DependOnInheritedWidgetState didChangeDependencies:$hashCode");
super.didChangeDependencies(); }}class CustomInheritedWidget<T> extends InheritedWidget {
CustomInheritedWidget({Key key, this.data, Widget child}) : super(key: key, child: child);
final T data;
// Define a convenient method for widgets in a subtree to get shared data
static CustomInheritedWidget<T> of<T>(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomInheritedWidget<T>>();
}
@override
bool updateShouldNotify(CustomInheritedWidget oldWidget) {
return oldWidget.data != data;
}
}
Copy the code
At the beginning, the text is 0. When the button is clicked, the value is +1 and the page is refreshed.
I/flutter (24452): BodyWidgetState build:293287865
I/flutter (24452): DependOnInheritedWidget:698685804
I/flutter (24452): DependOnInheritedWidgetState didChangeDependencies:185018106
I/flutter (24452): DependOnInheritedWidgetState build:185018106
I/flutter (24452): CustomRaisedButton build:266734984
Copy the code
Through print log we find DependOnInheritedWidgetState call didChangeDependencies () method, we have to analyse, what happened when click on the button.
_inheritedWidgets
The transfer
When Element is activated and mounted, the _updateInheritance method is called to direct _inheritedWidgets to _inheritedWidgets of _parent. The InheritedElement overrides this method, and the code looks like this
Mount () is the first time an element is added to the Element Tree, and then the element becomes active. Activate () re-marks the deactivate element as active.
In short, from nothing to something or from invisible to visible.
//Element
void _updateInheritance() {
assert(_active); _inheritedWidgets = _parent? ._inheritedWidgets; }Copy the code
//InheritedElement
@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
[InheritedElement] InheritedElement copies _parent’s _inheritedWidgets and saves the widget’s runtimeType as key and this as value in its inheritedWidgets property. So the UI tree of the original code looks something like this
BodyWidget
-CustomInheritedWidget
–Column
—DependOnInheritedWidget
—Builder
—-CustomRaisedButton
CustomInheritedWidget copies the BodyWidget’s _inheritedWidgets into its own _inheritedWidgets and saves itself in it, Then the _inheritedWidgets of Column, DependOnInheritedWidget, Builder, and CustomRaisedButton point directly to the _inheritedWidget of CustomInheritedWidget s
InheritedElement
The search for
Call in DependOnInheritedWidgetState CustomInheritedWidget of method, Of method invocation context. DependOnInheritedWidgetOfExactType < CustomInheritedWidget < T > > () can find CustomInheritedWidget, code is as follows:
//Element
@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;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor ! =null); _dependencies ?? = HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
Copy the code
//InheritedElement
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
Copy the code
We can see that in dependOnInheritedWidgetOfExactType this method, the generic Widget to find corresponding InheritedElement, save the ancestor variable, DependOnInheritedWidget add its ancestor to the _dependencies collection and save itself to the _dependents collection where the InheritedElement updates its internal data. You can notify all widgets that rely on this InheritedElement. The DependOnInheritedWidget’s _dependencies collection has its ancestor added, so when the DependOnInheritedWidget is destroyed, You can remove yourself (DependOnInheritedWidget) from the _dependents collection of ancestors to avoid unnecessary updates and memory leaks.
conclusion
When Element is activated and mounted, the _updateInheritance method is called to direct _inheritedWidgets to _inheritedWidgets of _parent. InheritedElement overrides this method, saving itself in _inheritedWidgets, so that when you’re passing down the hierarchy, the _inheritedWidgets contain all of the inheritedElements in the process. Then when obtained through dependOnInheritedWidgetOfExactType InheritedElement method the caller to hold yourself to a InheritedElement _dependencies collection, In addition, the method caller adds a reference to the InheritedElement in its own _dependencies collection, and deletes itself from the InheritedElement’s _dependencies collection when the method caller destroys it. Avoid unnecessary updates and memory leaks.
To consider
Element
theactivate
andmount
When is the method called?- in
CustomInheritedWidget
When changing internal data, whyDependOnInheritedWidgetState
Will be calleddidChangeDependencies()
?