Earlier we talked about the use and usage of the InheritedWidget, and we left a small question. These links

How does the Flutter framework know if child widgets are dependent on inheritedWidgets?

InheritedWidget definition

Take a look at the InheritedWidget definition first:

abstract class InheritedWidget extends ProxyWidget { /// Abstract const constructor. This constructor enables subclasses  to provide /// const constructors so that they can be usedin const expressions.
  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.
  ///
  /// When this widget is rebuilt, sometimes we need to rebuild the widgets that
  /// inherit from this widget but sometimes we do not. For example, if the data
  /// held by this widget is the same as the data held by `oldWidget`, then we
  /// do not need to rebuild the widgets that inherited the data held by
  /// `oldWidget`.
  ///
  /// The framework distinguishes these cases by calling this function with the
  /// widget that previously occupied this location in the tree as an argument.
  /// The given widget is guaranteed to have the same [runtimeType] as this
  /// object.
  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
Copy the code

Inherits from ProxyWidget and contains the createElement() and updateShouldNotify methods. The updateShouldNotify comment is complete and defines whether the notification subtree is required. Let’s look at what InheritedElement is returned by createElement() :

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; }... }Copy the code

One _parent? What is._inheritedWidgets? Here are some of the attributes Element has:

Map<Type, InheritedElement> _inheritedWidgets;
Copy the code

It holds the mapping between the InheritedWidgets that appear in the ancestor node and their corresponding Elements. During the Mount and active phases of Element, the _updateInheritance() method is executed to update the mapping.

For a normal Element instance, _updateInheritance() simply saves the parent Element’s _inheritedWidgets properties in its own _inheritedWidgets. So as to realize the layer upon layer transfer of mapping relationship.

@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’s _updateInheritance is different. The InheritedElement instance adds its own information to the _inheritedWidgets property. So that its descendant element can get a reference to that InheritedElement through the aforementioned _inheritedWidgets pass-through mechanism.

With that in mind, look at how the InheritedWidget does update notifications.

The InheritedWidget updates the notification mechanism

Mentioned above, want to get “recently” InheritedElement, you need to call BuildContext. InheritFromWidgetOfExactType. What does that have to do with Element? In fact, Element is BuildContext. The source code definition is as follows:

abstract class Element extends DiagnosticableTree implements BuildContext 
Copy the code

The next see BuildContext. InheritFromWidgetOfExactType source code:

@override
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;
  returnnull; } @override InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) { assert(ancestor ! = null); _dependencies ?? = HashSet<InheritedElement>(); _dependencies.add(ancestor); ancestor.updateDependencies(this, aspect);return ancestor.widget;
}
Copy the code

First look in the _inheritedWidget map to see if there are instances of a specific type of InheritedWidget. If so, the instance is added to its own dependency list and itself is added to the corresponding dependency list. This way the InheritedWidget knows which widgets that depend on it need to be notified using its _dependents attribute when updated.

Every time an InheritedElement instance is updated, the updated method in the InheritedWidget is called:

/// Calls [Element.didChangeDependencies] of all dependent elements, if
/// [InheritedWidget.updateShouldNotify] returns true.
///
/// Called by [update], immediately prior to [build].
///
/// Calls [notifyClients] to actually trigger the notifications.
@override
void updated(InheritedWidget oldWidget) {
  if (widget.updateShouldNotify(oldWidget))
    super.updated(oldWidget);
}
Copy the code

Updated method of ProxyWidget

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

Back to notifyClients in the InheritedWidget:

@override
void notifyClients(InheritedWidget oldWidget) {
  assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
  for (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;returnancestor == this; } ()); // check that it really depends on us assert(dependent._dependencies.contains(this)); notifyDependent(oldWidget, dependent); } } @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) { dependent.didChangeDependencies(); }Copy the code

< span style = “box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 14px! Important; word-break: inherit! Important;” Its didChangeDependencies() will not be called because it does not depend on ShareDataWidget.

To summarize, first execute the updateShouldNotify method on the appropriate InheritedWidget to determine if notification is required, If the method returns true, loop over the Element in the _dependents list and execute their didChangeDependencies() method. This way updates in the InheritedWidget are notified to child widgets that depend on it.

With the source code analysis behind us, there may be some more specific uses of inheritedWidgets in the future.


If you think this article is useful to you, please share and like it. Thanks a million.