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 ChangeNotifier
. ChangeNotifier
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 the 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 and 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 theInheritedWidget
.
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 Stateful/Inherited Widgets only, but if you’re making big/complex apps with lots of screens and a 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ć
For more Flutter-related articles follow Jelena on medium https://jelenaaa.medium.com/