State Management in Flutter.
Tags: flutter, android, ios, codenewbie
Published: 2022-01-07 13:12:36

Managing **state** in an application is one of the most important and necessary processes in the life cycle of an application. The main job of a UI is to represent **state**. According to the [official Flutter documentation](https://docs.flutter.dev/development/data-and-backend/state-mgmt/ephemeral-vs-app):

> In the broadest possible sense, the state of an app is everything that exists in memory when the app is running. This includes the app's assets, all the variables that the Flutter framework keeps about the UI, animation state, textures, fonts, and so on.

From this definition, we can then conclude that the pattern that programmatically establishes how to track changes and how to broadcast details about states to the rest of your app is known as **state management**.
The two state types that are normally considered are **ephemeral state**(also known as **_UI state_** or **_local state_**)and **app state**(sometimes also called **_shared state_**):

- **Ephemeral state** is the state you can neatly contain in a single widget. Ephemeral state is used when no other component in the widget tree needs to access a widget's data. Examples include whether a `TabBarView` tab is selected or when tracking the current progress of a complex animation. In other words, there is no need to use state management techniques (ScopedModel, Redux, Provider, etc.) on this kind of state. All you need is a `StatefulWidget`.
- State that is not ephemeral, that you want to share across many parts of your app, and that you want to keep between user sessions, is what is called **_application state_**. Application state when other parts of your app need to access a widget's state data. One example is an image that changes over time, like an icon for the current weather. Another example is when accessing login info that may be required in more than one section of your Flutter application.

Now that we've gotten the basic definitions out of the way, let's get to work exploring the various options we have at our disposal to manage the application state.

![GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxcaet663t3crzqz5ois.gif)

---

## Managing state in a Flutter application.

### 1. InheritedWidget.
According to the [official documentation](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html):

> InheritedWidget class is a base class for widgets that efficiently propagate information down the tree.

It's the basis for a lot of other state management widgets. If you create a class that extends `InheritedWidget` and give it some data, any child widget can access that data by calling `context.dependOnInheritedWidgetOfExactType<class>()`. 
Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.

```
class SampleWidget extends InheritedWidget {
  final Sample sample;
  SampleWidget(Key? key, required this.sample, required Widget child}) :
  		super(key: key, child: child);

  @override
  bool updateShouldNotify(SampleWidget oldWidget) => sample!= oldWidget.sample;

  static SampleWidget of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<SampleWidget>()!;
}
```

One can then extract data from that widget. Since that's such a long method name to call, the convention is to create an `of()` method. Then a child widget, for example, a text field that displays the sample text, can just use:

```
SampleWidget sampleWidget = SampleWidget.of(context);
print(sampleWidget.sample.text);
```

An advantage of using `InheritedWidget` is it's a built-in widget so you don't need to worry about using external packages.

A disadvantage of using `InheritedWidget` is that the value of a Sample can't change unless you rebuild the whole widget tree because `InheritedWidget` is immutable. So, if you want to change the displayed sample text, you'll have to rebuild the whole `SampleWidget`.

### 2. Provider.
According to the [official documentation](https://pub.dev/packages/provider):

> Provider is a wrapper around [InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html) to make them easier to use and more reusable.

`Provider` was designed by Remi Rousselet to wrap around `InheritedWidget`, simplifying it. In short, Provider is a set of classes that simplifies building a state management solution on top of `InheritedWidget`.
Provider has several commonly used classes such as: `ChangeNotifierProvider`, `Consumer`, `FutureProvider`, `MultiProvider` , `StreamProvider`, etc.

**ChangeNotifier**
`ChangeNotifier` is a class that adds and removes listeners, then notifies those listeners of any changes. You usually extend the class for models so you can send notifications when your model changes. When something in the model changes, you call `notifyListeners()` and whoever is listening can use the newly changed model to redraw a piece of UI, for example.

**ChangeNotifierProvider**
`ChangeNotifierProvider` is a widget that wraps a class, implementing `ChangeNotifier` and uses the child widget for display. When changes are broadcast, the widget rebuilds its tree.

```
ChangeNotifierProvider(
  create: (context) => SampleModel(),
  child: <widget>,
);
```

**Consumer**
`Consumer` is a widget that listens for changes in a class that implements `ChangeNotifier`, then rebuilds the widgets below itself when it finds any. When building your widget tree, try to put a `Consumer` as deep as possible in the UI hierarchy, so updates don't recreate the whole widget tree.

```
Consumer<SampleModel>(
  builder: (context, model, child) {
    return Text('Hello ${model.text}');
  }
);
```

If you only need access to the model and don't need notifications when the data changes, use `Provider.of`, as shown below:

```
Provider.of<SampleModel>(context, listen: false).<method name>
```

`listen: false` indicates you don't want notifications for any updates. This parameter is required to use `Provider.of()` inside `initState()`.

**FutureProvider**
`FutureProvider` works like other providers and uses the required `create` parameter that returns a `Future`.

```
FutureProvider(
  initialData: null,
  create: (context) => createFuture(),
  child: <widget>,
);

Future<SampleModel> createFuture() async {
  return Future.value(SampleModel());
}
```

A `Future` is handy when a value is not readily available but will be in the future. Examples include calls that request data from the internet or asynchronously read data from a database or network.

**MultiProvider**
What if you need more than one provider? You could nest them, but it'd get messy, making them hard to read and maintain. Instead, use `MultiProvider` to create a list of providers and a single `child`:

```
MultiProvider(
  providers: [
    Provider<SampleModel>(create: (_) => Model()),
    Provider<SampleDatabase>(create: (_) => Database()),
  ],
  child: <widget>
);
```

**StreamProvider**
Provider also has a provider that's specifically for [streams](https://dart.dev/tutorials/language/streams) and works the same way as `FutureProvider`. Stream providers are handy when data comes in via streams and values change over time like, for example, when you're monitoring the connectivity of a device, or listening to active changes from a network.

### 3. Riverpod.
According to the [official documentation](https://pub.dev/packages/riverpod):

> Rivepod can be considered as a rewrite of [provider](https://github.com/rrousselGit/provider) to make improvements that would be otherwise impossible.

Riverpod was written by Provider's author, Remi Rousselet, to address some of Provider's weaknesses. In fact, you'd be surprised to find out that Riverpod is in fact an anagram of Provider.
Riverpod is a very powerful state management library that allows you to:

- Easily **_create_**, **_access_**, and **_combine_** providers with minimal boilerplate code.
- Write **_testable code_** and keep your logic outside the widget tree.
- Catch programming errors at **_compile-time_** rather than at runtime.

By design, Provider is an improvement over [`InheritedWidget`](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html) and it depends on the widget tree. This leads to some drawbacks such as:

1. Combining Providers is very verbose.
2. Getting Providers by type and runtime exceptions.

To combat this, Riverpod is completely independent of the widget tree and doesn't suffer from any of these drawbacks.

### 4. Redux.
According to the [official documentation](https://pub.dev/packages/redux):

> Redux is a **_predictable state container for Dart and Flutter apps._**

Redux uses concepts such as actions, reducers, views, and store, whose flow is shown below:

![https://blog.novoda.com/introduction-to-redux-in-flutter/](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pnixqawu10g62obzt9pc.png)

In summary, actions, like clicks on the UI or events from network operations, are sent to reducers, which turn them into a state. That state is saved in a store, which notifies listeners, like views and components, about changes.

### 5. BLoC.
According to the [official documentation](https://pub.dev/packages/bloc):

> The goal of this package is to make it easy to implement the `BLoC` Design Pattern (Business Logic Component).This design pattern helps to separate presentation from business logic. Following the BLoC pattern facilitates testability and reusability. This package abstracts reactive aspects of the pattern allowing developers to focus on writing the business logic.

Think of it as a stream of events: some widgets submit events and other widgets respond to them. BLoC sits in the middle and directs the conversation, leveraging the power of streams.

![https://www.dbestech.com/tutorials/flutter-bloc-pattern-examples](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6pmrnrgd5wn05mdq3ds4.png)

### 6. MobX.
According to the [official documentation](https://pub.dev/packages/mobx):

> **_MobX_** is a library for reactively managing the state of your applications. Use the power of observables, actions, and reactions to supercharge your Dart and Flutter applications.

MobX utilizes the following concepts in its functioning:

- **_Observables:_** Observables represent the reactive-state of your application. They can be simple scalars to complex object trees.
- **_Actions:_** Actions are how you mutate the observables. Rather than mutating them directly, actions add a semantic meaning to the mutations.
- **_Reactions:_** They are the observers of the reactive-system and get notified whenever an observable they track is changed.

![https://itnext.io/flutter-state-management-with-mobx-and-providers-change-app-theme-dynamically-ba3b60619050](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0oacc0n2m5ipw9bl6dg.png)

### 7. GetX.
According to the [official documentation](https://pub.dev/packages/get):

> GetX is an extra-light and powerful solution for Flutter. It combines high-performance state management, intelligent dependency injection, and route management quickly and practically.

GetX uses two different state managers: the simple state manager (GetBuilder) and the reactive state manager (GetX/Obx).

```
class SampleController extends GetxController {

     @override 
    void onInit() {
       // Here you can fetch you product from server
       super.onInit();
    }

    @override 
    void onReady() {
       super.onReady();
    }

    @override
    void onClose() { 
          // Here, you can dispose your StreamControllers
          // you can cancel timers
          super.onClose();
    }
}
```

![https://kauemurakami.github.io/getx_pattern/](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/892f3h749m8kdghpthsn.png)

**References.**

- https://medium.com/flutter-community/making-sense-all-of-those-flutter-providers-e842e18f45dd
- https://codewithandrea.com/articles/flutter-state-management-riverpod/
- https://blog.novoda.com/introduction-to-redux-in-flutter/
- https://itnext.io/flutter-state-management-with-mobx-and-providers-change-app-theme-dynamically-ba3b60619050
- https://dev.to/gunathilakahashan10/getx-a-superior-state-management-in-flutter-4jcl

---

While that is a long list of state management options, they are by no means the only options out there, and thus the choice solely depends on your particular use case. 
That was indeed all that I have to share for now. To all readers, cheers to code🥂, and have a blessed day.