What is InheritedWidget
This is described in the documentation notes for InheritedWidget:
Base class for widgets that efficiently propagate information down the tree.
Base class that effectively conveys information to subtrees in the rendering tree.
Access from app:
void main() { runApp(MyApp()); // runApp(CustomInheritedWidget()); }
Start build ing a rendering tree that MyApp() can see as the root node of the tree. If the root node's Widget is set to an InheritedWidget, all the subtree sub-nodes in the subtree can get the data shared within the InheritedWidget. When the data in the InheritedWidget changes, all child widgets that depend on that data will be rebuilt.
What's the usage?
Features around Sharing and Covariance
- Unify theme settings so that when you change the theme, all subpages change immediately.
- Sharing of project base data, such as user information, permissions, and other public data.
- State Management
- ......
Core usage process
- Custom CustomInheritedWidget inherits InheritedWidget and sets the data to be shared as a member variable. Override the updateShouldNotify method, which specifies when widget s that depend on this shared data will be rebuilt.
- Get the shared data in a widget that needs to rely on it, CustomInheritedWidget.of(context).data.toString(), data is custom shared data and can be other. Override the didChangeDependencies method, which is triggered when the shared data changes.
- Make Dependent a child widget of CustomInheritedWidget
Source Code Analysis
- InheritedWidget, parameter is a child, sets the dependency to child or the child's descendant. The core function of Widgets is to configure data for element ation, where InheritedElement is the class that really works. The updateShouldNotify method is also a method in the overridden InheritedElement to notify those who depend on it to update.
abstract class InheritedWidget extends ProxyWidget { const InheritedWidget({ Key key, Widget child }) : super(key: key, child: child); @override InheritedElement createElement() => InheritedElement(this); /// Whether the framework should notify widgets that inherit from this widget. @protected bool updateShouldNotify(covariant InheritedWidget oldWidget); }
- Enter InheritedElement
class InheritedElement extends ProxyElement { ... final Map<Element, Object> _dependents = HashMap<Element, Object>(); @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; } @override void debugDeactivated() { assert(() { assert(_dependents.isEmpty); return true; }()); super.debugDeactivated(); } /// Returns the dependencies value recorded for [dependent] @protected Object getDependencies(Element dependent) { return _dependents[dependent]; } /// Sets the value returned by [getDependencies] value for [dependent]. @protected void setDependencies(Element dependent, Object value) { _dependents[dependent] = value; } /// Called by [dependOnInheritedWidgetOfExactType] when a new [dependent] is added. @protected void updateDependencies(Element dependent, Object aspect) { setDependencies(dependent, null); } /// Called by [notifyClients] for each dependent. @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) { dependent.didChangeDependencies(); } /// Calls [Element.didChangeDependencies] of all dependent elements, if /// [InheritedWidget.updateShouldNotify] returns true. @override void updated(InheritedWidget oldWidget) { if (widget.updateShouldNotify(oldWidget)) super.updated(oldWidget); } /// Notifies all dependent elements that this inherited widget has changed, by /// calling [Element.didChangeDependencies]. @override void notifyClients(InheritedWidget oldWidget) { assert(_debugCheckOwnerBuildTargetExists('notifyClients')); for (final Element dependent in _dependents.keys) { assert(() { // check that it really is our descendant Element ancestor = dependent._parent; while (ancestor != this && ancestor != null) ancestor = ancestor._parent; return ancestor == this; }()); // check that it really depends on us assert(dependent._dependencies.contains(this)); notifyDependent(oldWidget, dependent); } } }
_ Dependents: Maintains a map that holds references to all dependents.
**_ inheritedWidgets:**This is a property of the ancestor class Element, which only works if it is InheritedElement, which holds the corresponding relationship between inheritedWidgets and InheritedElements that occur in the ancestor node_ inheritedWidgets[widget.runtimeType] = this, where widgets. RuntimeType is an attribute in Object and represents this class only, where this is the InheritedElement.
**_ updateInheritance():** This method is also an Element method, which is called within the mount() of the Element to update _ inheritedWidgets. It is important to note that this method has a default implementation in Element as follows:
void _updateInheritance() { assert(_active); _inheritedWidgets = _parent?._inheritedWidgets; }
That is, to determine if there is _in the parent node inheritedWidgets, if any, assigned to the _of the current elementation inheritedWidgets, so that each layer's elementation retains the previous layer's _ inheritedWidgets, which is why inheritedWidgets keep passing data down. InheritedElement overrides this method. Mainly this code: _ inheritedWidgets[widget.runtimeType] = this, update mapping. Add the current InheritedWidget page to the _ inheritedWidgets.
**getDependencies():**Get all dependencies**setDependencies():**Add new dependencies. When customizing an InheritedWidget, a static method of is defined to get the custom InheritedWidget, such as:
static MyInheritedWidget of(BuildContext context) { return context.inheritFromWidgetOfExactType(MyInheritedWidget); }
Go deeper into context. Inside the inheritFromWidgetOfExactType method:
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) { assert(_debugCheckStateIsActiveForAncestorLookup()); final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType]; if (ancestor != null) { assert(ancestor is InheritedElement); return inheritFromElement(ancestor, aspect: aspect); } _hadUnsatisfiedDependencies = true; return null; }
Continue deep into inheritFromElement ():
@override InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) { return dependOnInheritedElement(ancestor, aspect: aspect); } @override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) { assert(ancestor != null); _dependencies ??= HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect); return ancestor.widget; } @protected void updateDependencies(Element dependent, Object aspect) { setDependencies(dependent, null); }
The final fix is: set Dependencies (dependent, null); Thus, when a dependency uses of to get a custom InheritedWidget, it adds itself to the dependency collection. ** updateDependencies():** Update the specified dependency notifyDependent():, call the dependency didChangeDependencies method, notify that the dependent data has changed, and this method will be called in bulk by the notifyClients() method. notifyClients():, Bulk inform the dependant that the data has changed. This method is defined in ProxyElemnt and overridden in InheritedElement. When data changes within a custom InheritedWidget, an override method, updateShouldNotify, is used to define whether a notification dependency update is required, such as:
@override bool updateShouldNotify(MyInheritedWidget oldWidget) { // Returns true when old and new data are inconsistent, notifies the child widget that depends on this widget, and the didChangeDependencies method in the child widget is called return oldWidget.data != data; }
Notifications are made when old and new data are different, where return can be defined based on actual conditions. So when does the updateShouldnotify method call? Within the updated method in the InheritedElement:
@override void updated(InheritedWidget oldWidget) { if (widget.updateShouldNotify(oldWidget)) super.updated(oldWidget); }
The updated() method is defined in the ProxyElement, where super is called. The notifyClients () method is implemented in ProxyElement as follows:
@protected void updated(covariant ProxyWidget oldWidget) { notifyClients(oldWidget); }
NotfyClients () is an empty implementation in ProxyELement and is overridden in InheritedElement. This brings the whole process together.
InheritedWidget internal process
- Customize the InheritedWidget, set shared data, override the updateShouldNotify method, and provide of static methods.
- Make a dependency a descendant of InheritedWidget, which is internally added to the list of dependencies when the dependency calls the of method to get shared data dependents.
- The element tree is built by mount ing through _ updateInheritance method_ inheritedWidgets layer-level download, in which a widget of type other than InheritedWidget will send parent's _ inheritedWidgets are assigned directly to themselves, while widgets of type InheritedWidget will assign parent's _ inheritedWidgets give themselves and add them in.
- Shared data changes, the updated method determines whether updateShouldNotify is true, calls the notifyClients method for true, and notifyClients calls dependent internally. DidChangeDependencies(); The call is ultimately made to the didChangeDependencies method of the dependency.