Flutter State Management Basics and Useful Resources

Flutter state management is a broad subject and it can be hard to make sense of it all.

In this tutorial, I'll help you understand the basics by explaining what is state and the difference between local and shared/global state.

And I'll give you an overview of the built-in widgets that you can use to manage state in Flutter, along with links to the best resources from the official Flutter documentation.

You can go quite far and build non-trivial applications with the built-in Flutter state management widgets. It's worth learning how they work before moving to more complex solutions.

Ready? Let's go!

What is state?

Flutter apps are built with a declarative programming model.

This means that widgets define their UI by overriding the build() method, which is a function that converts state to UI:

UI = f(state)

In this context, the Flutter documentation defines state as:

whatever data you need in order to rebuild your UI at any moment in time

We could also say that the UI is all about how your application looks like. And the state is about how your application behaves.

In principle, this is a simple concept:

state => UI

What is complex is that there are many different ways of transforming state into UI, with different pros and cons.

Not only that, but there are two different kinds of state: local state and global state.

Local vs Global State

As we know, we can compose Flutter widgets into a widget-tree that represents our application's UI:

Widget tree for the example Flutter counter app
Widget tree for the example Flutter counter app

Sometimes you have state that can be self-contained inside a single widget. That state is known as local or ephemeral state and Flutter offers some built-in tools such as setState and StatefulWidget to deal with this.

If you have some simple state that only affects the behaviour of a single widget, StatefulWidget is all you need. This video from the official Flutter channel explains how to use it:

On the other hand, when state needs to be shared across multiple widgets or even the entire app, you're dealing with shared / global app state.

Sharing state across multiple widgets is where things get interesting and there are many different techniques to do this. So let's review what Flutter offers out-of-the-box:

InheritedWidget

You can use Flutter's built-in InheritedWidget to share state across multiple widgets, as explained in this video:

InheritedWidget is all about making some data or state available to multiple widgets via scoped access. For example, inside your widgets you can call Navigator.of(context) to access the main Navigator and this uses InheritedWidget under the hood:

Scoped access with InheritedWidget
Scoped access with InheritedWidget

Implementing your own InheritedWidget subclasses is not easy and quite error-prone. For this reason, the provider package was introduced.

ValueNotifier & ChangeNotifier

Flutter includes a ValueNotifier class as a way to store your state outside your widgets. You can use it with a ValueListenableBuilder widget to update the UI when the state changes. This is best explained here:

Here's an example showing how to use ValueNotifier and ValueListenableBuilder together:

final valueNotifier = ValueNotifier<int>(42); ValueListenableBuilder<int>( valueListenable = valueNotifier, // called when the value changes builder: (context, value, _) { return Text('Value is $value') } );

Flutter also comes with ChangeNotifier, which is the "cousin" of ValueNotifier. For a practical explanation of how to use it, see this page.

FutureBuilder & StreamBuilder

Many asynchronous APIs use Futures and Streams to notify your app when new data is available.

Their difference is that a Future produces a single asynchronous value, while a Stream can produce many asynchronous values over time.

Both Futures and Streams are part of the Dart SDK. My Complete Dart Language course covers them in great detail.

You can use Flutter's FutureBuilder widget to decide what widget to show depending on the state of the Future (loading, data, or error):


Similarly, use Flutter's StreamBuilder widget to rebuild your UI when a stream emits new data:

When loading asynchronous data from a Stream-based API, it's good practice to check for these UI states: data, no data, error, loading.

StreamBuilder supports this but it is quite clunky to check for data or errors inside the builder's snapshot:

final stream = Stream.fromIterable([21, 42]); StreamBuilder<int>( stream: stream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.active) { if (snapshot.hasData) { return MyWidget(snapshot.data); // data } else if (snapshot.hasError) { return MyErrorWidget(snapshot.error); // error } else { return Text('No data'); // no data } } else { return CircularProgressIndicator(); // loading } } )

3rd party packages such as flutter_bloc, Provider, and Riverpod offer alternatives to StreamBuilder that are much easier to use.

Using Flutter's built-in widgets

You can go quite far with the built-in StatelessWidget, StatefulWidget, and InheritedWidget classes. You can use ChangeNotifier or ValueNotifier to manage your state, or FutureBuilder and StreamBuilder when reading data from asynchronous APIs.

This official guide shows how to put things together using ChangeNotifier and ChangeNotifierProvider to build a simple shopping cart application:

Wrap Up

The built-in widgets we covered are at the basis of state management in Flutter. Over 40 state management packages now exist, but they all build on the same principles and foundations.

So take the time to understand the basics by exploring the resources I shared.

And once you're ready to dig deeper, I recommend learning about Riverpod, a powerful reactive caching and data-binding framework that was born as an evolution of the Provider package.

I've covered Riverpod extensively on this site, and the best place to start is here:

Happy coding!

Want More?

Invest in yourself with my high-quality Flutter courses.

Flutter In Production

Flutter In Production

Learn about flavors, environments, error monitoring, analytics, release management, CI/CD, and finally ship your Flutter apps to the stores. 🚀

Flutter Foundations Course

Flutter Foundations Course

Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.

Flutter & Firebase Masterclass

Flutter & Firebase Masterclass

Learn about Firebase Auth, Cloud Firestore, Cloud Functions, Stripe payments, and much more by building a full-stack eCommerce app with Flutter & Firebase.

The Complete Dart Developer Guide

The Complete Dart Developer Guide

Learn Dart Programming in depth. Includes: basic to advanced topics, exercises, and projects. Last updated to Dart 2.15.

Flutter Animations Masterclass

Flutter Animations Masterclass

Master Flutter animations and build a completely custom habit tracking application.