Using Flutter’s InheritedWidget to hold app state

Lately, I have been studying the different architecture patterns proposed for developing flutter applications, including those based on a redux store. Almost all of them make use, in some way or another, of the InheritedWidget pattern and I wanted to know how well it would adapt to the task.

The InheritedWidget is heavily used in flutter itself to rebuild widgets in the tree when a widget higher up in the hierarchy changes.

The documentation page says the following for InheritedWidget:

“Base class for widgets that efficiently propagate information down the tree.
To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.inheritFromWidgetOfExactType.
Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.”

But, is the InheritedWidget a good candidate to hold the redux state instance?

This is a quick write up to try to answer that question and better understand how the pattern works behind the scenes. It is not an exhaustive explanation and leaves out a lot of details that might provide more insight. The goal is to have a basic grasp of its operation.

All the information here has been extracted from the framework.dart file.

We can break the analysis in three steps:

  • How do widgets know about the available InheritedWidgets in the ancestor line?
  • How do widgets subscribe to changes in one of those InheritedWidgets?
  • When an InheritedWidget changes, how is that change transmitted to the dependent widgets down the tree?

Let’s tackle these questions one at a time.

How do widgets know about the available InheritedWidgets in the ancestor line?

A widget/element has always at this disposal a reference to the list of InheritedWidgets available in its ancestor line. They are stored in the _inheritedWidgets member together with its corresponding type.

Map<Type, InheritedElement> _inheritedWidgets;

When a new widget/element is added to the tree (or reactivated) _inheritedWidgets is updated to the value in the parent.

void activate() {

_updateInheritance();

}
void _updateInheritance() {
_inheritedWidgets = _parent?._inheritedWidgets;
}

If an InheritedWidget is added, _updateInheritance() is called as above but additionally the widget adds itself to a new map by calling

_inheritedWidgets[widget.runtimeType] = this;

From here downward, the new map will be inherited, including the additional InheritedWidget.

How do widgets subscribe to changes in one of the available InheritedWidgets?

When a widget down the tree needs to depend on an InheritedWidget of a given type higher up in the hierarchy, the method inheritFromWidgetOfExactType(type) is called, normally inside the of(context) method. Here the following actions are performed:

  • The desired ancestor is searched in the _inheritedWidgets map
  • and the widget adds itself to the _dependents of the ancestor InheritedWidget.
InheritedWidget inheritFromWidgetOfExactType(Type targetType) {

final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {

ancestor._dependents.add(this);

}

}

So, every InheritedWidget instance ends up having (in _dependents) a list of descendant widgets down below the tree that depend on him.

How is the change in the InheritedWidget transmitted to the dependent widgets down the tree?

When an InheritedWidget changes, its update() method is called.

void update(newWidget) {

notifyClients(oldWidget);

rebuild();
}

The dependent widgets are notified of the change before the InheritedWidget itself is rebuilt. They are notified through the notifyClients() method.

void notifyClients(oldWidget) {
if (!widget.updateShouldNotify(oldWidget))
return;

for (Element dependent in _dependents) {

dependent.didChangeDependencies();
}
}

By overriding the method updateShouldNotify() you can decide what changes in the InheritedWidget are used to notify the clients.

In the descendant, the call to didChangeDependencies() marks the widget to be rebuilt in the next frame.

void didChangeDependencies() {

markNeedsBuild();
}

Notify state changes down the tree

One important thing to take from above is that with the internal notification scheme implemented in InheritedWidget [based onupdateShouldNotify()] all dependent widgets will be notified of the exact same change in the InheritedWidget.

This is not a problem if the app o redux state is small or has a small number of variables, as the example StateContainer class in “flutter_architecture_samples” shows.

But as your app grows, the app or redux state size grows accordingly. By placing the app state in an InheritedWidget and using updateShouldNotify() to compare the state as a whole, all dependent widgets will be notified of every single change in any of the state properties, even if a particular change does not apply to the widget in question. You are not able to filter the notification before the dependent build method is called.

What we need is flexibility to notify widgets down the tree selectively based on different changes in the state variables.

The first option is used by flutter extensively; use different InheritedWidgets for different state variables. This option though, is not compatible with a single redux state anymore.

If you want to stick to the redux model in big applications, you will have to architect the app around other patterns that use InheritedWidget indirectly, and are specifically designed for redux, like flutter_redux, flutter_built_redux and other. These libraries allow to filter based on specific changes. The StoreConnector in flutter_redux, for instance, checks if the state change applies to the widget and its ViewModel before proceeding to the build method.

Take a look at Brian Egans’ examples that implement different architectures based on redux and InheritedWidget.

Update (May-2018): An emerging pattern that has seen a grow in popularity is the combination of InheritedWidget and Streams. You can use a different stream for each type of update that you want to convey down the tree, thus only notifying the widget that is really interested in that change.

Refer to this talk in Google IO 2018 by Matt Sullivan and Filip Hracek.

Again, this technique is kind of incompatible with Redux because redux changes are advertised using a single stream.

So, what’s your choice? A single redux state that limits the options available to update the tree? or, the flexibility provided by streams although you may need to adapt your app state or app model a little more?

Update (Sept-2018): This article would need a sequel to talk about the new InheritedModel widget that allows to notify dependent widgets of only certain changes.