This article will explore Flutter’s built-in solutions for state management.

In all the examples below, we’ll be changing the app background by clicking on the button:

For the purposes of this article, imagine that our backgroundColor is some complex app state.

The first solution, of course, is a simple StatefulWidget:

We introduce the currentColor variable in the State object where, on every button click, a new value gets generated. By calling setState(), the whole UI gets redrawn.

This works well, but imagine that this color is something more complex. It would be much better if we could separate the UI from the logic (so we could easily change only the UI or only the logic later). Those layers should be separated for non-simple apps.

Now, we’ll introduce the ChangeNotifier:

We have moved our logic to the Notifier class, which is extended to ChangeNotifierChangeNotifier has the ability to notify its listeners that some change in it has occurred. When we detect that change in our listener method (in the initState() method), we redraw the whole UI by calling the setState().

OK, this is better, but should we redraw the whole UI every single time the color of our Container changes? Probably not.

This time, we’ll introduce a ValueNotifier:

ValueNotifier is basically a ChangeNotifier that holds only one value, and when it changes, those who listen to its changes get notified.

We could add listeners to the ValueNotifier object and redraw the whole UI, but there’s a much more convenient way of displaying changes now: ValueListenableBuilder!

ValueListenableBuilder is a Widget that gets redrawn when its ValueListenable (notifier in our example) changes value:

ValueListenableBuilder will automatically register as a listener of notifier changes and the builder method will be executed on each change. So now, when we press the button for a background change, only the builder method part will be executed and that part of UI will be redrawn — not the whole screen.

But what happens if you need to display your value changes in multiple places in your app, in different widgets that are in different files? Surely, you would not like to pass those values within Widget constructors.

That’s why we now introduce InheritedWidget.

We will define a ValueNotifier in it and then access it from different parts of our UI:

The code for accessing InheritedWidget’s fields looks like this:

In a nutshell, it’s saying the following: From builderContext and up in the tree, find an InheritedWidget of the type InheritedWidgetWithNotifier.

And now, UI looks like this:

So now, we’re able to access the ValueNotifier from different Widgets. Both “stripes” listen to the changes in ValueNotifier from InheritedWidget.

Conclusion

Everything can be done in Flutter with any kind of state management, but should it be done like that?

For simple apps, it is totally fine to go with even only Stateful/Inherited Widgets, but if you’re making big/complex apps with lots of screens and complex state that needs to be fetched from multiple widgets or screens, you should probably take a more advanced approach like provider or bloc.

Jelena Lečić

Flutter Team Leader
 

For more Flutter related articles follow Jelena on medium https://jelenaaa.medium.com/