As Bob continues this article series on Mobile App development from an Embedded System’s perspective, he tackles State and State Management in Flutter. As you will see, it is very different from what embedded engineers are used to when we think of state and that misunderstanding can cause no end of trouble if we don’t understand it.
Over the years, I found that designing embedded systems using state machines to be a useful and powerful paradigm. There are different methods of implementing a state machine (Complete code for these is found in the Resources for this article): a table of function pointers indexed by the state (see Figure 1 for a code snippet); the “switch-case” method (see Figure 2 for a code snippet); or when using an object oriented language like C++, you can encapsulate each state in a class to represent the state and it’s transitions (see Figure 3).
However, my hubris in thinking I was well versed in state machines did not prepare me for understanding state in either React Native or Flutter. As you may remember, I had two major problems with React Native: how it managed concurrency; and how it managed state. No surprise here. The same thing happened with Flutter, so let’s start defining terms and see if we can unravel the mystery of states and state management in Flutter.
Everything in Flutter is made up of Widgets. And there are two kinds of Widgets: State less widgets and Widgets that change based on the state of the User Interface (UI). These are called appropriately StatelessWidgets and StatefulWidgets respectively. But wait! What do they mean by state?
Reactive/Declarative vs Imperative UI
Hold that question. Turns out that my experience in embedded systems and Windows development did not prepare me for Flutter states and state management. In Windows you build a screen out of components like radio buttons or sliders. Once built, if the component changes, you must update it. With Flutter (and React Native), the underlying framework builds the whole user interface all at once. You never just update a component (or Widget). Windows is by definition Imperative UI and Flutter (and React Native) are Reactive or Declarative UI’s. I have been living in the Imperative paradigm (both in Windows development and in embedded systems development) and I have shifted gears without putting in the clutch.
Now, Let’s Talk State
From Flutter’s Reactive paradigm, there are two kinds of states: ephemeral states (also known as local or UI states) and app states. Ephemeral states are those that can be encapsulated within one widget. A checkbox is either in the state of on or off. Clicking the checkbox widget turns it on or off. But the state of a cart widget is more complicated because it is changed not by clicking on the cart but on clicking on other widgets (like an “Add to Cart” button). Thus, the state of a cart is an app state.
It is this connection between an “Add to Cart” button and the Cart that then requires some sort of management of state (what we call state management).
With all that aside, let’s define state as used in Flutter:
State is information that can be read synchronously when a widget is built or information that might change during the lifetime of a widget. [1]
Perhaps you can see where my trouble started. State is being used in a very different way than when we created a Finite State Machine in our embedded systems.
The creators of Flutter (and React Native) wanted to decouple the screens from the underlying state and created a framework where the designer did not need to manually change the state of the UI every time the state changed. Thus, whenever you change the state of a given object, you must indicate to the framework that a state has changed and let it decide what needs to change.
As we will see in some examples in a bit, it is one thing to change the ephemeral state of an object. It is another to change the app state as in the cart example I mentioned. This requires some sort of state management to accomplish the state change. From the Flutter documentation on state management [2]:
State management is how we organize our app to most effectively access state and share it across widgets.
Notice the operative words: “share it across widgets.” That’s like the “Add to Cart” button sharing cart information with the Cart.
Why Do We Need States
The question is often raised: Why do I need states? I haven’t needed them in my web designs or in my embedded designs. That comes from core design decisions that the Flutter design team made in the beginning when they created StatefulWidgets. Let’s look at an example you can run in Dartpad. (Just copy all the code from the main.dart in the Resources and place it in the Dartpad.com editor and click Run. NOTE: My apologies about putting all of the widgets in one file. A limitation of Dartpad! If you want to see the same code organized rationally in multiple files and multiple folders, check out the GitHub repository for this [3]). You should see a screen like Figure 4. (Sorry again, but Dartpad only creates web apps, so it doesn’t look like a phone. If you want to put it into Visual Studio Code and connect to a real phone, there are a lot more complications than we have time to cover in this short series. Trust me — this code runs on an Android and iOS phone. See Figure 5 for how it looks on my Android phone in landscape mode.) Click on your Subscription for Circuit Cellar which has been “Discontinued”. In the code, when the button is clicked, we change your status from Discontinued to Active (a good thing with your subscription to Circuit Cellar) by changing the text in variable buttonEn. But the Text doesn’t change! Why? Because the framework hasn’t been notified that the state changed via a call to setState (See Figure 6). Just changing the variable doesn’t change the state of the widget. Uncomment the call to setState (See Figure 7) and see how the button now works properly. That is why, in the Flutter framework you need states.
Why do I need State Management?
Since you cannot change a Widget from outside (for example, your “Add to Cart” button cannot just call the IncrementCart function in the Cart widget), you need some scheme for you to communicate to other stateful widgets. And that is done with state management. Let’s again illustrate this with an example from our code.
If you click on the Circuit Cellar Reading Plan with No State Management button, you will see a scrollable list of articles from Circuit Cellar to read (See Figure 8). Of course, Embedded in Thin Slices is at the top! Once you have read the article, just click on the article and it will get checked off. But wait! The progress bar is not updated. Because, like the cart, it is another widget and there is no way to change the widget from outside without state management. Interesting behavior when you go back to the main screen, and then back to our No State Management screen, the progress bar is updated. That is because the progress bar is re-built from existing state data.
State Management Tools
Flutter has provided a few state management specialized widgets to provide this functionality:
InheritedWidget: With this widget, you can define a number of objects that can be accessed by other widgets in the widget tree. In our example code, we defined a few: an integer – numberRead and a function – updateProgress. When the checkbox is clicked, the numberRead is updated by calling updateProgress. And because these are inherited by all the widgets under the widget tree, the progress bar is updated. Voila! State management. When the state of one widget changes, another gets notified. In our Dartpad application, you can click on the “Circuit Cellar Reading Plan – InheritedWidget” button to see that the progress bar now works.
InheritedNotifier: Perhaps the easiest way to distinguish this from the InheritedWidget is that the widget is notified when a value changes where as InheritedNotifier widgets are notified on demand. For example, in my MP3 streaming app, the progress bar does change when the position in the MP3 changes (it is changing continuously) but when I notify it that it changes – for example every second instead of every microsecond.
InheritedModel: Imagine that you had a data structure that was recording data from a number of sensors in an avionics package. You wouldn’t want the altimeter widget to get updated every time that the air speed changed. To accomplish this, the Flutter design team created the InheritedModel widget. With this you can take just some of the data, and limit who gets notified.
Houston! We have a Problem
Those and a few other state management tools were provided by the Flutter team. But if you go to the Flutter documentation by the Flutter team about state management they give you examples using other state management tools like: Provider and Riverpod. Why is that? I won’t get into the “state management wars” in cyber space, but they are fierce. I attempted to use Riverpod in my app and quickly got snowed. It was way too complicated for a bear of very little brain like me. But it seems to be the most popular state management paradigm. Provider is supposed to be a little higher level than InheritedWidget and easier to learn than Riverpod. So that sounded good to me. In the example code you will see the option to plan your Circuit Cellar Reading using Provider state management (click on the “Circuit Cellar Reading Plan – Provider” button). Let me briefly summarize how Provider works.
Provider
Given the dissatisfaction with the Flutter provided state management, an organization that calls themselves dashoverflow created the Provider state management package[4]. It is intended to be a higher level wrapper around the InheritedWidget and InheritedNotifier mechanisms provided. With Provider, you create a model for each object that you want to provide state management. For example, in our example code, we have a model for the checkbox (See Figure 9). This has a toggle function and an array of checkboxes and it has a way to increment and decrement the total articles read. Both the Progress widget and the Checkbox widget access these objects through the widget’s context. You can set up an object to be watched and be notified if it changes or you can set it to read the object. Remember when I said that the innards of a widget cannot be changed from the outside? With Provider they can be.
A Plethora of Options
The older I get, the less I like too many options. The official Flutter documentation points you to no less than 15 different outside packages for Flutter state management. How is that possible? Because everyone thinks they have a better way. All are doing basically the same thing. This creates a mess for re-using code. For my purposes in my Sermon Engagement app, my need for state management is with the audio player. The audio player has a progress bar widget that needs to be updated from several places: the internal player as the MP3 plays; the slider on the screen to move the progress bar; and the Bluetooth headset which changes the progress with fast forward and skip. I avoided state management tools with React Native by using (horror of horror) globals. React Native allowed that. But I couldn’t get the same global implementation to work with Flutter.
The package that supported Bluetooth headset control used one flavor of state management and the package that provided example code for the progress bar used Riverpod. I attempted to use them, but found that trying to blend them I ended up with mud, and not-working mud at that. This is where re-usability suffers when the core design doesn’t support a basic feature in a robust way.
Conclusion
Something tells me that the Flutter development team dropped the ball. They created a framework that required state management but they provided something that proved to be inadequate by the user community.
My recommendation is to avoid state management at all costs wherever possible. Keep It Simple Smarty.
REFERENCES
[1] https://docs.flutter.dev/get-started/flutter-for/react-native-devs
[2] https://docs.flutter.dev/get-started/fundamentals/state-management
[3] https://github.com/rjapenga/cc_eits_smd (or Circuit Cellar Embedded in Thin Slices State Management Demo. In the /lib directory you will find all of the code organized rationally)
[4] The “Flutter Favorite” Provider package makes state management “easier.” https://pub.dev/packages/provider
SOURCES
A good description of Imperative versus Reactive/Declarative programming in Flutter. https://www.dhiwise.com/post/declarative-ui-vs-imperative-ui-in-flutter-development
For an architectural overview of Flutter, check out this resource: https://docs.flutter.dev/resources/architectural-overview
For getting started with Flutter, I found this helpful even if you are not a React Native developer or a JavaScript programmer. https://docs.flutter.dev/get-started/flutter-for/react-native-devs
Another great place to start with Flutter can be found here: https://www.youtube.com/watch?v=xWV71C2kp38&list=PLjxrf2q8roU3wk7CDw4RfV3mEwOJbjx1k&index=4
This is a good resource of the Flutter design team’s perspective on state management. https://docs.flutter.dev/data-and-backend/state-mgmt/intro
There are scores of options of outside paradigms of state management here: https://docs.flutter.dev/data-and-backend/state-mgmt/options
For a fairly clear introduction to state management with Provider, this is fairly clear: https://www.dhiwise.com/post/achieving-optimal-performance-with-flutter-provider-state-management
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • APRIL 2025 #417 – Get a PDF of the issue
Sponsor this ArticleBob Japenga has been designing embedded systems since 1973. From 1988 - 2020, Bob led a small engineering firm specializing in creating a variety of real-time embedded systems. Bob has been awarded 11 patents in many areas of embedded systems and motion control. Now retired, he enjoys building electronic projects with his grandchildren. You can reach him at
Bob@ListeningToGod.org










