Stateful Widgets

Kefeh Collins
Flutter Community

--

We saw in our previous article (Stateless widgets: check it out here) the details of what state is and what stateless widgets are. It looked fascinating and exciting but kind of limiting, look at the clock example for example, if we are to have the clock turning only when there is an external input then that defeats the whole purpose of the clock doesn’t it? We need a mechanism where the internal state of the clock continues to change when a trigger is applied and self-reflecting the changes, we, therefore, need a widget whose state changes.

State: state defines the condition of a system or entity at a specific time (or under specific conditions).

note that when we talk about stateless and stateful widgets we are referring to the widgets as the system and the state of that system are the internal workings of the widget itself not of the app as a whole.

The problem with state-changing (statefulness) is not only that we want the state to change, but most often that we need the state to change based on the previous state(keep track and know the previous state), and we need to update the view (rebuild the widget) when the state changes.

  • State needs to change (keeping track of previous state value).
  • Need to self-rebuild the widget to showcase the new changes.

We see this caveat with stateless widgets in that when we try to rebuild a widget, we often find out that we lose every internal state (values of its properties) as the widget is always composed of the information provided for it when it is built and the build is often triggered externally.

Let’s take our BlueSquare example. We want that this square is clickable and shows the number of times the square has been clicked.

Here is the thought, since we want each BlueSquare Object to be clickable, it makes sense that the BlueSquare class should provide this clicking functionality.

Rather than

Because the

  • second method obviously requires more code than the first,
  • The parent widget holding a BlueSquare which is wrapped with the GestureDetector will need to have some sort of way to track the value of the number of clicks itself and if they are more than one BlueSquare objects, it will need a way of differentiating which value belongs to which, that’s just so much heavy lifting for one class and it can become messy.
  • You will need an additional parameter in the constructor of the BlueSquare to use to build the BlueSquare object with the count.

As we have seen, the first method is therefore more preferable. So let’s try to make that clicking work.

Unfortunately, after our best attempt at making this work this way, when we run our app, it does not seem to update the number of clicks when the BlueSquare widget is clicked.

But if we add a print statement just below the clicks = clicks + 1;

clicks = clicks + 1;
print(clicks);

And run the code, we see that the console or terminal has the actual value updated from the clicks.

So why is the widget not showing the updated value?

Because there is nothing telling the widget to update its view (or rebuild itself) when the “clicks” value changes.

Can we have something that will trigger the rebuild of the widget when the value changes?

Yes, we can convert our Stateless Widget into a Stateful widget, inheriting from the StatefulWidget and State classes that provide methods that help us do this.

Let’s have a look

We see now that our BlueSquare widget is now a stateful widget, with just its constructor and an overridden function “createState” that creates the state for the widget. Alongside the Stateful Widget, we have a _BlueSquareState class that inherits from the State class defined for the BlueSquare Object, and as we can see, it holds the properties that change and the build method that defines the widgets that make up the BlueSquare widget.

_BlueSquareState class holds:

  • The properties of the widget that changes (behavior).
  • The layout of the widget (defined in the build method).

If we however run this code, we see that nothing actually changes, it’s still the same as before, no changes to the view even after a click, why is that?

Because we still are not telling the widget to rebuild (in our case the state) when the clicks property changes.

We can do this by using the function “setState()” provided for us by the State class.

This setState function gives us the possibility of setting properties inside the State class and also triggers the rebuild of the state class, as every property set inside the setState, will immediately confer that the State is dirty (or has changes that might impact the interface) and needs a rebuild with the new changes.

Re-running our code, and viola, clicks or taps on the BlueSquare widgets are actively conferring the changes in the number of clicks.

It’s worth noting however that the BuildContext and the State have a direct relationship when it comes to stateful widgets, and the state is most often associated with the buildContext of the widget (as we saw, a state is a different class from the actual widget and the state is the actual place where the layout is built, so to be able to build the layout we need to know where it should be located in the widget and element tree and we often use the BuildContext to have that information. Now we need to ensure that the buildContext of the widget is associated with the state, so that we can have information that the buildcontext provides).

Alongside the setState and the build method, there are other methods that the State class provides for us to help us manage the entire configuration of our own state class.

  • The initState(): This function is called immediately after the createState function, (that is after the state has been created and associated with the Widget), and before the build method is called. It is called exactly once per build of our state class. This method is important as it serves as the point for which everything that the build method will depend on are initialized and or created, but if that thing that needs to be initialized needs to have access to the BuildContext then you can not do that here, because it is only after calling the super.initState() function when you override the initState function, that the state is associated with the BuildContext. For example, if you have some stream of data that will be built in your build method, you can initialize that here, also things like controllers and listeners could be initialized here.
  • The didChangeDependencies(): This method is called directly after the initState() function and just before the build method and most often the caveat of the initState() function that you don’t have access to the context is often solved here, as the BuildContext is available at this instance and can be used for those things that require the buildContext. Sometimes we instead take those dependencies for widgets that need the BuildContext and place it in the body of the of build method, It is however very advisable to do that here in the didChangeDependencies() and not in the body of the build method because we need the build method to have a single responsibility which is to build the layout of the widget
  • The dispose(): The dispose() method is called when the widget is discarded. We often use this method if we need to perform some cleanup (e.g. listeners, controllers…), then invoke the super.dispose() right after.

There are other methods provided to us by the State class like the didUpdateWidget() which are not directly related to state and so I did not discuss them here.

This brings us to the end of the stateful widgets. In our next article, we will be taking a look at flutter hooks and its importance, and how it can be used to manage state in flutter.

Follow Flutter Community on Twitter: https://twitter.com/FlutterComm

--

--

Kefeh Collins
Flutter Community

An Enthusiastic problem solver, uses code as a tool for problem solving.