We will discuss
setState(); the easiest and simplest state management approach in Flutter. Many great state management libraries are available in Flutter, but it is important to know the most basic approach to update the UI state of your app.
You might be tempted to jump to learning BLoC, Provider, Riverpod, and the thousands of other frameworks available, but learning the fundamentals is more important than complex solutions. Once you understand the basics, you might even decide to make your own state management framework.
What is the setState method?
Stateful widgets have this method called
setState() which tells the Flutter framework that this
StatefulWidget's state has been updated and needs to be replaced.
setState() method is called, the
State of the
StatefulWidget gets replaced with a new one, where the updates inside declared inside the
setState() function will be applied.
How do you use setState?
It is good practice to call
setState() from one-time events such as button presses or other user-input triggers.
To make setState work as intended, you have to update a state in your app inside the callback argument of this method. For example, if we want to increase the counter when pressing a button and want to display this updated value on the screen, we can write the code as such:
In the sample code, the state that we want to update is the
_counter member variable. Every time we tap the button, the counter is updated by one. Subsequently, the widget is redrawn from the screen and uses this updated value.
setState() method should not be called in the
build() function. The reason is that every time you call
setState() and there is a new state, the build function will be called recursively.
Simple Example - Changing the theme colour
Let's put our
setState() knowledge into practice by building a simple desktop app that changes its theme when pressing a button. In this example, we will put the whole app in one file to see how
setState() works the best.
setState() only works on
StatefulWidget so for this simple example, we will build our app as a
We built the
SimpleThemeColorApp widget which extends a
StatefulWidget to add an option to save state. The state of this app is the
_materialColor which defaults to blue.
In order to update the theme, we need to add an
onTap listener in the buttons that call the
setState() method. Do not forget to add the callback that sets the new value of the
Updating the state without setState
You might think that you can just set the
_materialColor property without
setState(). If you do this, the
_materialColor property will be updated; the screen however will not.
The reason behind this behaviour comes from the idea that widgets are immutable. Widgets cannot change once rendered BUT they can be replaced.
StatefulWidget's state through
setState() triggers the rebuilding of this widget. Once this widget has been rebuilt, you can see the updated display.
Complex example - Changing the theme colour outside the StatefulWidget
It gets a bit trickier when you want to update the state of a widget from another widget. Let's look at an example where the
Theme of the app is declared at the topmost parent widget of the tree.
If we have independent and separate widgets for different screens in the app, they would not have access to the state of the topmost parent widget. So how do we update the state from these widgets?
We can pass callbacks as part of the widget's constructor parameters. These callbacks can be defined when the parent widget creates these child widgets and pass the
setState() function altogether.
In the code example, our topmost parent widget is the
ComplexThemeColorApp. The state member of this widget (
_ComplexThemeColorAppState) keeps the theme colour variable
_materialColor which is used to set the theme colour of the app.
The child widget
PageWidget will be the screen that contains the buttons to change the theme. Note that when constructing this widget, it requires a function parameter called
updateThemeCallback to update the state. This function accepts a
MaterialColor as a parameter.
Let's define the
PageWidget as a row containing two buttons; one for each colour. Since this widget has the callback, we can set this as the function to be called when we tap each of the buttons, each with its corresponding colour.
The resulting app will be the same as the one shown in the basic example and only the code implementation is different.
You will notice that this approach is not scalable. It works on widget trees that are one, or even two levels deep, but not more than that. Adding callbacks can be a nightmare especially when you start building complex widget trees.
The ideal approach to solve this problem is for a widget to be able to access a state within its scope, update it, and the widgets listening to the state will be reconstructed and updated.
Enter the state management frameworks
setState() method is best used for updating the state of a single
StatefulWidget. It's time to use an appropriate state management framework when your app starts to become more complex.
The Flutter development team has suggested a number of frameworks to use to handle states. I have mentioned these libraries in the introduction (BLoC, Provider, Riverpod, etc) and are all well-suited for handling states in Flutter.
We will discuss these better state management frameworks in another article.
Widgets are immutable by nature therefore we need to rebuild the widgets if we want to display a new state. The simplest way to update the state of a
StatefulWidget is by using
setState(). To use
setState(), update the member variable of your widget in the callback function of
setState(). This will rebuild the widget with the new value which then updates the display of your app.