Managing State in Flutter
Sep 22 2022 · Dart 2.17, Flutter 3.0, Android Studio Chipmunk
Part 1: Understand State Management
02. Understand State Management
As mentioned in the previous episode. State management is large and sometimes confusing topic with lots of strong opinions. But, before we tackle the various solutions, it’s best for us to define our problem. That is, what is state management and what problem is it trying to solve?
If you’ve done any reading about Flutter, you will have seen this diagram. Flutter is a UI toolkit that takes a very functional approach. Our user interface results from the data being passed into it. That is, provide the same input and you’ll always get the same output.
This has been a recent trend as of late, spawning from react based frameworks. That is, we don’t construct our user interfaces act and behave when being used, rather our user interfaces reflect the state of our data.
This means when our data changes then our user interface should appropriately reflect it. In Flutter we do this by creating individual widgets and connect them together like lego bricks. This creates what is known as a widget tree. When state changes in a widget, that part of the tree is torn down and then rebuilt from scratch to reflect the current data.
Now, if you’ve been following along with our learning path, you know that there are two widgets out of the box. The stateless widget and the stateful widget. The stateless widget does contain state, but that state doesn’t change.
For instance, you many have a widget that prints the username on the screen. But wait, the user may change their username and earlier I said that the user interface in Flutter reflect the data. Does the stateless widget update to reflect this data?
The answer is - yes - but it’s immutable state Once it is set, it can’t be changed. When the user changes their name, the UI state change will trigger a widget rebuild.
The stateless widget containing the user name will be destroyed and a new stateless widget is created with the new name passed into it. The stateless widget contains the new name, but that widget has no way to change it. Throughout its lifetime, it keeps the same name until it is destroyed by a widget rebuild whereby a new widget is created with a different name.
You’ve also seen stateful widgets. That’s where the magic happens. Unlike the stateless widget, a stateful widget can contain changing fields.
But wait - if actually add a mutable field to a Stateful Widget, you’ll run into a compile error because Stateful widgets are actually immutable.
But, when you create a stateful widget, the widget creates a private class sidecar that contains all of the changing state.
When a state rebuild occurs, that stateful widget like the stateless widget is destroyed, and a new one is replaced.
Yet, our state sidecar remains and is associated with the new stateful widget. Thus, our state persists from rebuild to rebuild.
The state associated with a widget is known as ephemeral state or local state. That is, the state is localized to widget So with stateful widget and stateless widget, we have a lot of tools to reflect and update our user interface based on our data. Except, there are times when we want our data persist higher in the widget tree or in different areas of our app. This is known as app state. This is where state management comes into play because how and why we accomplish this is different depending on the library that you choose to use.
For now, let’s not think about third party libraries. We’re going to handle everything using just the Flutter framework. So here’s a problem you’ll run into with Flutter. Here we have two different widget trees. One tree wants to write the user’s name. The other tree wants to access the user id. This means we need our state accessed in two different parts of the tree. To do this, we lift state up to common ancestor so both widgets can access it.
We can then pass state down by using constructors, but you’ll that this is not an optimal solution. Now some of you may be thinking putting your state outside the widget tree and in what is known as the global space. This is known as polluting the global namespace which by its name alone tells you its a bad idea. By making your state globally accessible. This can cause issues with testing and it exposes your state from everywhere which means, anything can access and change your state. Instead, aim to put your state at the top of the widget tree. This means, we will want to have a state management solution that allows access to child widgets but doesn’t require the intermediary widgets to know about the state