Flutter State setState, context, widget and mounted
By: Greg Perry
Tags: Flutter, iOS App Development, Mobile App Development, Android App Development, Programming

The Classic State

In the gif above, you see we’re going up through the three pages incrementing their individual counters along the way. There’s a number of buttons offered so to navigate up and down the routing stack. As anticipated, returning back down the pages then up again, the counter on the third page is reset to zero. This makes sense. The app had retreated back down the routing stack, and the State object retaining the counter (the state) on the third page was terminated (its dispose() function called). Perfectly normal. However, notice the second page is keeping its counter value?? How is it doing that?!

Further note, on the second and third pages, you can increment the counters on the previous page! On the third page, for example, there are two buttons to increment both the second page and the home page. Well, that’s neat. Now how is that done? Finally, pressing that ‘Home Page New Key’ found all the way up on page three results in the counter on the first page (the Home page) to be reset to zero! Now, what’s going on there?

Granted, this is such a rudimentary example, but it does demonstrate some fundamental aspects involved in the State Management of a typical Flutter app. I’ll put to you that all the frameworks offered, today, use the same basic mechanism to provide such capabilities.

Use the setState() function to a particular State object.

Let’s examine the first State object responsible for displaying a screen to the user. In this case, it’s the ‘Home page’ greeting the user with the first counter. Below is a screenshot of that State with little red arrows highlighting points of interest. What do you see?

First and foremost, you can see the example app is using a subclass of the State class called, SetState. (Update: SetState is now a Mixin! More useful.) Yes, this class is the main point of interest in this article. Certainly using a subclass of the State class now relieves us of that one annoying warning message about using the setState() function (Update: It’s annoying again! It’s a Mixin!).

You’ll find all the frameworks out there have a ‘subclass’ of the State class in one form or another. All of which supplying a means to use the setState() function to a particular State object — whether the developer knows exactly which State object that is or not is another matter.

What else do you see? Well, there's “the separation of work” that I personally live by when developing code. More specifically, there’s always a separate and dedicated class (_HomePageBloc in this case) that’s responsible for the actual ‘business logic’ involved in the app while the build() function in some widget somewhere is responsible for the interface.

Further, there’s the degree of abstraction that I always implement as the API between these separate areas of responsibility. As an example, the actual ‘counter’ here in this app is concealed by the class property, data. I follow the consistent practice of naming instance fields after the parameter used by the receiving Widget. As it happens, the Text widget’s first parameter is named data. All this being a consistent approach of doing things — dare I say, I’m following a design pattern.

Now, in keeping with the topic at hand, let’s examine two points of interest in particular. What I’ll be presenting from now on will be concepts subtle in nature but very important characteristics to uphold when writing good code.

The three Bloc classes (no direct relation to the BloC design pattern) you’ll find in this example are indeed the ‘Business Logic Components’ for the app. Each has its own little bit of responsibility (their own little bit of ‘state to manage’). They’re also the app’s event handlers — each response to particular events that may be triggered by the user or by the system (the phone) itself.

The screenshot below presents the first Bloc somewhat named after the State object it’s to work with. It even explicitly takes in the type of that State object it works with. At a glance, you can see this Bloc class is for the home screen.

Now, why is the Bloc simply taking in ‘the type’ of the State object? Why not take in the very State object itself? Seems to be another rouned-about way of doing things, no? Lastly, note the Bloc class is found in the same Dart library file with its leading underscore. For demonstration purposes, this was the case. However, in practice, I’d suggest the ‘business logic component’ deservers its own Dart file — and that’s the case with production code.

There’s another question here. Why was an altogether separate function called onPressed() created in the State object? See below.

I mean, in some design patterns offered today, it’s customary to simply find the corresponding ‘onPressed’ VoidCallback function in the Bloc class and use that instead as depicted in the screenshot below.

However, when you do see such functions in a State object, know that this object is now providing an API. Therefore, it must be necessary for external players to also trigger an event in this object. You’ll soon realize who these players are in this particular example app. Hint: Buttons.

Let’s backtrack a bit and take a deep dive into the instantiation of this first Bloc class, _HomePageBloc. In the screenshot below, you can see we’ve jumped ahead a bit to examine this Bloc class. In fact, we’ll examine all three of them. You can see all three Blocs in this app take advantage of inheritance extending from the common parent class, _CounterBloc. After all, they all pretty much do the same thing and so that function is found in one parent class — working with an integer called, counter.

Again, it would have been better to have these Blocs in their own separate Dart library files. For example, if they were in their own file, you’d then be free to extend a different parent class altogether in the future — displaying string ‘word pairs’ let’s say while unaffecting the rest of the app. The parent class, _CounterBloc, should be in its own Dart library file as well preferably with a more generic class name. A degree of abstraction always makes for easier maintenance of an app in production.

Regardless, note the first Bloc class, _HomePageBloc, doesn’t know the type of State object beforehand, and so that type is passed in as a generic type. While in the second Bloc class, _SecondPageBloc, the State type is known and is explicitly specified. Which approach to use, of course, depends on the circumstance. At least, you’re free to use either. You have that option.

Further note, the second class utilizes a factory constructor, and that’s how it retains its count even when you retreat back down the routing stack! In every Flutter app you write, returning to a previous page will remove a Page from the stack, and if it’s represented by a StatefulWidget, that means the StatefulWidget’s State object will be disposed of. Every time. Unless you do something about it.

You’ll note, when it comes to the second page, the ‘State Management’ has been allocated to a separate class altogether and not left to the State object. Following the Singleton design pattern, the _SecondPageBloc class remains in resident memory for the life of the app. Thus keep its counter (its state) and is assigning a brand new State object to itself (the second arrow) whenever a user comes back to that page.

Now, let’s look at the third and final Bloc class, _ThirdPageBloc. Note that the instance field, state, is successfully overridden with a getter. THIS IS HUGE! Do you know why? It’s huge you can successfully override a mutable instance field with an immutable getter! Since a getter is essentially ‘instantiated’ only when it’s first used, you can provide the ‘type of state’, but you’re not obligated to instantiate a reference to that State right then and there! You can wait. Possibly in some situations, the State is not to be instantiated at that point — It may not be available for some reason.

_CounterBloc class

We might as well take a look at that parent class to the Bloc’s now. Again, it’s an event handler. Such a class is required to respond to events. In this case, the most profound event is when a user taps on the plus sign to increment the counter. Thus, the most important capability of this class is to then notify the appropriate State object when it’s completed responding to that event.

As you know, it does have access to the ever-important setState() function for a particular State object. It takes advantage of that access and even defines its own setState() function — for any other modules to then call to notify the app and reflect a change. Finally, it offers a corresponding dispose() function to be called in its State object’s own dispose() function when the State object itself indeed terminates during the course of the app’s lifecycle. It’s all nice and compact. However, we could do better.

Note, such abilities, on the whole, should be present in any and all modules that are to work with a SetState object in this fashion. Such abilities should be readily available to any class you may define to contain the ‘business logic’ of an app. Such a circumstance would therefore be a good candidate for a mixin, no? A screenshot of that mixin is below.

That parent class, _CounterBloc, has now been changed — focusing truly now on the one lone functional responsibility assigned to it in this particular app. When it increments its counter, it then notifies the rest of the app with the setState() function. It now takes in the mixin using the keyword, with, resulting in code generally being more modular. Further, as you see below, there’s a higher cohesion in the resulting class.

Navigating The State

Looking at the third page in the routing stack, we see it presents to the user five buttons. The first three buttons literally affect ‘the state’ in three separate regions in the app. The first button calls upon a provided Bloc object to respond to the event of incrementing that page’s counter. The next three buttons involve the State objects from ‘previously visited’ areas of the app. Each is responsible for retaining its own state. Note, the names of the VoidCallback functions of the State objects, homeState, and secondState: onPressed. Should the fourth and last State object also have such a function?