Rings for modern men

Is your man a big fan of jewelry as a whole and rings in particular? You’re a lucky gal then because you know what thing will make a perfect present. Still, you need to pick a ring carefully. If you…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Managing Flutter Application State With InheritedWidgets

Everyone has heard that interactive applications can be decomposed into three parts: model, view, and controller. Anyone who has given Flutter a test drive will be familiar with its react-style support for building view/controllers with widgets and callbacks. Everyone does not seem to be so clear on how Flutter supports the “model” part of MVC.

A Flutter application’s model represents its persistent state. Widgets provide visualizations of that state and they enable the user to modify it. A widget depends on the model when its build method retrieves values from the model or when its callbacks modify model values. If a widget depends on the model, it should be rebuilt if the model changes. Exactly how one gets that to happen is this article’s subject.

The approach for building MVC apps outlined in this article is not the one and only way to do so. There are many ways to bind Flutter to models and if you’re planning something big, you might want to survey your options, some of which I’ve listed at the end of this article. On the other hand, even if you don’t decide to adopt the little ModelBinding class presented in the final section, you may learn something new about Flutter by reading this article.

This isn’t an introductory Flutter article, I expect that readers will already have some experience with the Flutter API. For example, it’s assumed that:

Hopefully it’s obvious how one could extend the values that the model represents.

This is an immutable model, so changing it means replacing it. The MVC approaches shown below can be used with a mutable model, it’s just a little bit more complicated that way.

This is the simplest approach to integrating a model, perfect for an afternoon app.

A StatefulWidget is associated with a persistent State object. The State object’s build method creates the subtree contributed by the widget, just like the StatelessWidget’s build method does. The State object’s setState method causes the widget to be built again, the next time the display’s frame rate interval has elapsed. If the stateful widget’s state object owns the model, its build method configures the widgets it creates using model values and it calls setState when the model changes.

Defining the example’s ViewController stateful widget requires a little boilerplate:

In this case the widget’s persistent state is a Model instance. The build method creates a button that shows the model’s current value and replaces the model when pressed. The updateModel method resets the currentModel and calls setState to trigger a rebuild. In other words, pressing the button updates the model and then rebuilds the ViewController widget.

The approach outlined above is a strawman. It’s not ideal for a relatively large scale application:

Hopefully it’s clear that StatefulWidgets, while well suited for creating widgets with some internal persistent state, aren’t ideal for sharing a model in a complex application. And, as software engineers, complex is our bread and butter.

The InheritedWidget class has some special properties that make it well suited to sharing a model with a tree of widgets:

You’ve probably run into the Theme inherited widget already. The static Theme.of(context) method returns the Theme’s ThemeData and records context as a Theme dependent. If the Theme is rebuilt with a new and different ThemeData value, then all of the widgets that referred to it with Theme.of() are rebuilt automatically.

It’s easy enough to use a custom InheritedWidget subclass in the same way, as a host for the application’s model. This InheritedWidget subclass is called ModelBinding because it connects the application’s widgets to the model.

The updateShouldNotify method is called when the ModelBinding is rebuilt. If it returns true, then all of the dependent widgets are rebuilt.

The BuildContext inheritFromWidgetOfExactType() method is used to lookup an inherited widget. Because it’s a little ugly, and for other reasons that will become apparent later, it’s usually wrapped in a static method. Adding the lookup method to the Model class makes it possible to retrieve the model from any descendant of ModelBinding with: Model.of(context):

Now ViewController can be stateless and its reference to the model becomes:

Any descendant of ModelBinding can do the same thing, there’s no need to pass the Model downwards. If the model changes then dependent widgets like ViewController are rebuilt automatically.

The ModelBinding itself must be built by a stateful widget. To change the model, that stateful widget must be rebuilt by calling its setState method. Now the top level App widget becomes stateful and a callback that updates the model must be passed along to the ViewController.

In this case, Model is just a simple immutable value, so replacing it is as simple as assigning a new currentModel. Replacing it could be more complicated, for example if the model contained objects that required lifetime management, replacing the model might require disposing parts of the old model.

On the upside, the InheritedWidget version of our model binding makes it easy for widgets to refer to the model and it automates rebuilding those “dependent” widgets when the model changes.

On the downside, it’s still necessary to plumb an updateModel callback down through the widget tree, to widgets that need to change the model.

This version of the InheritedWidget approach for binding widgets to a model simplifies model updates. Now any descendant of ModelBinding can get the model as well as update it, and there’s no need to pass around a model specific callback.

So, as before, any descendant of ModelBinding can get a model value with Model.of(context), and by doing so become an automatically rebuilt model dependent. Now any descendant of ModelBinding can update the model with Model.update(context, newModel). Which is nice.

To enable rebuilding the model with the static Model.update() method, it’s necessary to introduce an extra stateful widget. This gets a little complicated, so if you’re wearing a hat, hold on to it.

Any descendant of ModelBinding can now update or change the model using these methods. The example’s button does both:

This version is generally useful and it weighs in at well under 100 lines of code. It can be used as is, or as a starting point for a more complex model binding system.

There is one limitation that really needs to be eliminated: the Model data type has a private connection to the ModelBinding implementation. Apps should be able to define their model types any way they like, they shouldn’t need to extend the Model class defined here. To address that, we’ll add a type parameter.

This version factors out the Model type. Now the type of the app’s model is ModelBinding’s type parameter, and ModelBinding provides the generic static functions of<T>(context) and update<T>(context).

The implementation is a bit more complicated (again) for the type parameter, which is why it was left for the finale. On the up side, the implementation is still very small, about 64 lines of code, and using the ModelBinding widget class and its two static methods should be very straightforward.

The application creates a MyModel type ModelBinding like this:

The application’s model, MyModel, is retrieved and updated with generic versions of the of and update methods like this:

The upshot is that ModelBinding et al. can be safely factored into a tiny library, rather than being wired into the application. Only the ModelBinding<T> class needs to be exported.

This article was intended to explain the basics of Flutter’s StatefulWidget and InheritedWidget classes and to show how the latter could be used to bind an application’s model to its widgets. There’s much more to share about both classes and about related classes like InheritedModel and InheritedNotifier. All of that will have to wait.

Add a comment

Related posts:

La arquitectura es un producto corporativo que todos adquirimos

En contraste a otros campos de la cultura como la moda o bien la música, la arquitectura solo puede ser vivida y entendida en persona. Para las grandes marcas corporativas, diseñar un nuevo edificio…

Every Man needs an Egypt

Every man needs an Egypt,. “Every Man needs and Egypt” is published by Eli Elefant.

I am not fast enough

I spent the last 2 hrs watching videos of high speed trains that operate in different parts of the world today.. “I am not fast enough” is published by Arohi Chakraborty.