Source code for Flutter InheritedWidget

InheritedWidget is a Widget that provides data sharing, where data is passed from top to bottom and shared data is available to any child Widget. The Flutter source uses a lot of inheritedWidgets to transfer data from top to bottom, such as Theme, DefaultTextStyle, MediaQuery, etc.

The Theme doesn’t directly inherit the InheritedWidget, but its internal implementation uses the InheritedWidget anyway.

The creation process of the InheritedWidget

We’ll start with Element’s updateChild, and I’ll omit a lot of the code that isn’t relevant to the InheritedWidget.

Element > updateChild

Here the inflateWidget is called to create an Element.

@protected Element updateChild(Element child, Widget newWidget, dynamic newSlot) { Element newChild; . newChild = inflateWidget(newWidget, newSlot); . return newChild; }Copy the code

Element > inflateWidget

Create a new Element using createElement in the newWidget and call Element’s mount method.

@protected Element inflateWidget(Widget newWidget, dynamic newSlot) { ... // create a new element final element newChild = newwidget.createElement (); // Call newChild's mount method newChild.mount(this, newSlot); return newChild; }Copy the code

Element > mount

Call _updateInheritance, where the InheritedElement overrides the _updateInheritance method.

@mustCallSuper
void mount(Element parent, dynamic newSlot) {
	...
	_updateInheritance();
}
Copy the code

InheritedElement > _updateInheritance

The _inheritedWidgets are defined within an Element, the Key is the runtimeType of the InheritedWidget, and the value is the InheritedElement

Map<Type, InheritedElement> _inheritedWidgets;
Copy the code

Take the _inheritedWidgets of the parent Widget, create an empty HashMap if they are null, or create a new HashMap with the data inside. We don’t add the _inheritedWidgets directly to the parent element so that only the children of the InheritedWidget can get its data.

Void _updateInheritance() {var inheritedWidgets final Map<Type, InheritedElement> incomingWidgets = _parent? ._inheritedWidgets; if (incomingWidgets ! = null) _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets); else _inheritedWidgets = HashMap<Type, InheritedElement>(); / / / save the current element to the _inheritedWidgets, the key is InheritedWidget runtimeType, the value is the current InheritedElement. _inheritedWidgets[widget.runtimeType] = this; }Copy the code

The creation process ends here.

Get the data for the InheritedWidget

Child widgets for InheritedWidget there are two ways: one is the getElementForInheritedWidgetOfExactType, this method when InheritedWidget trigger updates will not update the current widget. And there’s a dependOnInheritedWidgetOfExactType, the method to obtain InheritedWidget when InheritedWidget triggered update after will trigger the update of the current widget.

Here’s a Theme

final _InheritedTheme inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
Copy the code

element > dependOnInheritedWidgetOfExactType

This method needs to pass in a generic T that inherits the InheritedWidget. As described in the _updateInheritance method, the InheritedElement will save itself in the _inheritedWidgets, with the InheritedWidg as the key Et runtimeType. We can use the generic T to get the InheritedElement saved in _inheritedWidgets.

@override T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) { [InheritedElement final InheritedElement ancestor = _inheritedWidgets == null] [InheritedElement ancestor == null] null : _inheritedWidgets[T]; if (ancestor ! () {$inheritedElement () {$inheritedElement (); aspect) as T; } _hadUnsatisfiedDependencies = true; return null; }Copy the code

Element > dependOnInheritedElement

Save the current element in an InheritedElement _dependents. This is a HashMap where the Key is Element and the Value is Object. This method calls the didChangeDependencies method of all elements in _dependents when InheritedElement is updated.

class InheritedElement extends ProxyElement {
  final Map<Element, Object> _dependents = HashMap<Element, Object>();
}
Copy the code

Here is the ancestor of InheritedElement, this element is call dependOnInheritedWidgetOfExactType method.

@override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {{Object aspect}) / / / call InheritedElement updateDependencies method. Ancestor. UpdateDependencies (this aspect); return ancestor.widget; }Copy the code

InheritedElement > updateDependencies

void updateDependencies(Element dependent, Object aspect) {
	setDependencies(dependent, null);
}
Copy the code

InheritedElement > setDependencies

Save the called Element in the _dependents of the called InheritedElement.

@protected
void setDependencies(Element dependent, Object value) {
	_dependents[dependent] = value;
}
Copy the code

The renewal of the InheritedWidget

When an update occurs to the InheritedWidget, some widgets that use the InheritedWidget are triggered. Is partially because obtained through getElementForInheritedWidgetOfExactType InheritedWidget widget will not receive InheritedWidget update notification.

Let’s start with Element’s updateChild.

Element > updateChild

Call element’s update method.

@protected Element updateChild(Element child, Widget newWidget, dynamic newSlot) { ... child.update(newWidget); . }Copy the code

InheritedElement > update

The updateShouldNotify method on the InheritedWidget is called and returns a value to determine whether to initiate notification.

@override void updated(InheritedWidget oldWidget) {/// If the InheritedWidget's updateShouldNotify returns true, if the InheritedWidget's updateShouldNotify returns false (widget. UpdateShouldNotify (oldWidget)) / / / here call ProxyElement updated super. Updated (oldWidget); }Copy the code

ProxyElement > updated

Send out notifications for updates.

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

InheritedElement > notifyClients

Iterate over all elements of _dependents.keys and call notifyDependent.

@override void notifyClients(InheritedWidget oldWidget) { for (final Element dependent in _dependents.keys) { notifyDependent(oldWidget, dependent); }}Copy the code

InheritedElement > notifyDependent

Call Element’s didChangeDependencies method to trigger the update.

@protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {/// call Element's didChangeDependencies to mark the current Element to be updated. // mark StatefulElement's _didChangeDependencies as true, / / / element performRebuild trigger based on _didChangeDependencies judgment whether the state. The didChangeDependencies dependent. DidChangeDependencies (); }Copy the code

StatefulElement > didChangeDependencies

void didChangeDependencies() { super.didChangeDependencies(); // mark _didChangeDependencies as true _didChangeDependencies = true; }Copy the code

Element > didChangeDependencies

Element’s markNeedsBuild method is called to trigger the update.

Void didChangeDependencies() {rebuildelement markNeedsBuild(); }Copy the code

This is where the update process ends.