Enhance Flutter state management with Riverpod

Take your Flutter development to the next level with Riverpod: A Journey into advanced state management

Shirsh Shukla
10 min readJun 12, 2023

In this article, we’ll be exploring Riverpod in-depth. We’ll cover all the important parts of Riverpod, any limitations you might face, and effective solutions to overcome them. Let’s dive in!

let’s start this by understanding why we need Riverpod in the first place, there are some problems with a provider which cannot be fixed some of those problems include confusing and tedious syntax from proxy provider multiple providers not having the same return data type and the infamous provider not found an exception.

To counter these problems riverpod was written however riverpod is not just a state management solution anymore it is also a replacement for many design patterns like Singleton, dependency injections, and service locators it also helps in fetching caching and canceling Network requests while also handling the error cases to get started head to pubspec.yaml file and make sure you have the latest version of flutter riverpod installed.

riverpod is divided into three packages hooks_riverpod flutter_riverpod and riverpod, hooks_riverpod is used with flutter hooks flutter_riverpod with the flutter SDK and the riverpod with dart programs, which means you can create cli using riverpod

.

after having that in place go to the main.dart file wraps your root Widget the widget that’s passed in the run app with a widget known as providerscope this provides a scope widget that is a constant and it comes from the flutter riverpod package this provider scope keeps track of all the providers and ensures that there is no leakage of State in spite of the providers being declared globally.

Global Declaration of providers or variables in general means if suppose I have a variable like string “title” and I’ve declared it outside of any function and outside of any class I’ve declared it globally usually Global declaration be it of variables or providers should be a problem because of mutability issues meaning if a non-constant variable like this

the title is declared its value can be changed by any function for example if I have the main function right here I can change the value of the title over here

right, so I can type my title as flutter_riverpod, so imagine this for hundreds and thousands of files you are able to change this, so do you think it’s a good practice absolutely not because it would be very difficult to know which function changed the value of the title and if we ever want it or not and to find it will be a big task and we generally don’t want to do that so you might think if this is prohibited why does provider or riverpod even do this,

The reason for that is the providers declared globally in riverpod are immutable and this is for all the providers in riverpod they all are immutable except one, so let’s discuss all types of Providers one by one.

Provider

The first type of Provider is the provider itself, riverpod has multiple providers which can give us access to the State whatever state we want to access for them, each provider has its own purpose so the very first type of provider is providing it is called provider.

if you are coming from a provider package you already know about it as the name suggests

it is an object that provides data to Widgets or other providers it is a read-only widget and cannot update the value inside of it it can be used to provide primitive non-primitive data types and even instances of classes to create a provider.

While a Provider in Riverpod is useful for exposing values and services to other parts of your application, it does not inherently provide a way to manage state. This is where the StateProvider comes in.

StateProvider

so for that, we have the second type of Provider which is the State provider, State provider is used to update the value from outside which is not possible using the provider it is like an upgrade over the normal provider.

The StateProvider is a type of provider that allows you to expose a value that can be read and modified across your application. When you modify the value, any widgets that are watching it will be rebuilt to reflect the updated value.

now there comes a third type of Provider which StateNotifierProvider basically, it is an upgrade over the state provider, basically state provider is used for very simple values like a string that we had over here and integer values double values a Boolean values but what about complex values for example if you want to modify or manipulate the values inside of a class what if I want to update a map there will be many functions, I’m not saying we can’t do that with the state provided it’s definitely possible but our logic will be lying around in our widgets so we don’t want to make make our logic lie around in the widgets as programmers we want our logic to be together in a single place and most likely a class.

StateNotifierProvider

This is where StateNotifierProvider comes in handy. By using a StateNotifier class to manage complex states, we can encapsulate all the logic for updating that state in one place. This allows us to separate concerns and keep our code organized and maintainable.

For example, let’s say we have a User class that contains various properties such as name, email, age, and address. We could use a StateNotifierProvider to manage the state of the User object and provide a clean and organized way to modify and access its properties throughout the app.

Here’s an example of how we could use a StateNotifierProvider to manage the state of an User object:

so the next type of provider is the changeNotifierProvider, if you are using a provider then you already know about it because it directly comes from Riverpod to easily migrate from provider to Riverpod because many of the functions in the provider have changed Notifier class.

that’s why it’s there for easy transition but however, in the Riverpod documentation it’s mentioned that we shouldn’t use much of a changeNotifierProvider if you are building our application from scratch it’s always recommended to use a StateNotifierProvider.

changeNotifierProvider

but let’s for understanding, ChangeNotifierProvider is a widget in the Flutter Riverpod state management library that provides an instance of a ChangeNotifier to its descendants. Its purpose is to listen to changes in the provided ChangeNotifier instance and notify its descendants to rebuild when a change occurs.

Here’s an example of how you can use ChangeNotifierProvider Riverpod:

however, if you have a provider-used project and it’s pretty big to convert then you can use it but it’s not recommended and I would also recommend you to not do so now after having a brief knowledge of the change notifier provider, let’s jump to next type of provider is the future provider.

FutureProvider

FutureProvider as the name suggests is a provider based on HTTP calls or asynchronous code even Firebase calls so when this FutureProvider is used for futures.

so if you have asynchronous code you’ll definitely be using the future provider and it will make your life much easier it is kind of a replacement and a shorter code for future Builder and a better one in my opinion.

FutureProvider is a provider that allows you to expose a value that may not be available immediately but will be available at some point in the future. This is useful when you need to perform an asynchronous operation to obtain a value, such as fetching data from a server.

For example, you could use FutureProvider to fetch user data from a server and expose it to your app. The FutureProvider would handle the asynchronous operation of fetching the data and updating the UI once the data is available.

now after having this future provider let’s move on to the final type of provider in the Riverpod package which is the stream provider.

StreamProvider

A StreamProvider can be useful when you need to listen to an event stream, such as user interactions or changes to a database.

so as we see all types of providers, what are the features that are available to us and it’s not just for future providers or stream providers it’s available to every single provider.

I talking about well it’s the modifiers, modifiers is a function that is used to modify or enhance the behavior of a Provider. Modifiers are used to add additional functionality to Providers, such as caching, lazy initialization, or filtering.

Here are some examples of modifiers in Riverpod:

  1. .family: This modifier is used to create a family of Providers that share similar behavior. For example, you could create a family of HTTP Clients that share the same base URL.
  2. .autoDispose: This modifier is used to automatically dispose of a Provider when it is no longer being used. This can be useful for Providers that consume resources, such as database connections.
  3. .overrideWithValue: This modifier is used to override the value of a Provider with a new value. This can be useful for testing or for providing mock data.
  4. .state: This modifier is used to create a Provider that manages a mutable state value. This can be useful for managing UI state, such as whether a button is disabled or enabled.

like here is an example code snippet that shows how to use the .autoDispose modifier in a StreamProvider:

In this example, the StreamProvider exposes a stream of integers that emits a new value every second for five seconds. The .autoDispose modifier is set to true, which means that the provider will automatically dispose of the stream subscription when it is no longer needed, such as when the widget that uses it is removed from the widget tree. This helps to prevent memory leaks and unnecessary resource usage.

Modifiers can be combined to create more complex Providers with custom behaviors. For example, you could create a Provider that automatically disposes of itself and caches its value using the .autoDispose and .cached modifiers.

so now we are done with all the types of providers we have had six providers to look at to give you a refresh of that provider is a read-only object so if you want to provide a value be it primitive data types like integer Boolean string you can use them or non-primitive data type like list map whatever or even instances of classes. second state provider, it is like an upgrade of the normal provider if you just want to update a simple State like an integer value or a Boolean. the third one is the StateNotifier provider this is generally used when there are more complex states to be dealt with for example the user class and we want to modify their properties or whatever. the fourth one is changeNotifierProvider it’s recommended not to use by Riverpod it’s just for the transition from provider to Riverpod if you have a bigger code base this will help you but if you’re starting from scratch or you have a smaller project I would recommend you to not use it because of its mutable state. fifth one is the FutureProvider so if you have asynchronous code and you want to avoid the Roth of this future Builder and async snapshot you can use future provider. sixth one and the final one is StreamProvider so if you have a stream and of course, you want to get rid of the stream Builder and async snapshot again you can use stream provider it is reusable and with and it should help you a lot all right now after knowing all of the providers there are some methods on ref that I want to talk about.

ProviderObserver

the last thing that I want to talk about in this flutter riverpod package is something known as a ProviderObserver now I told you if there are Global variables it would be very difficult to understand where the provider is being added where it’s being removed where it’s being listened so it is quite a task but with access to this something known as a ProviderObserver you can log these things out.

ProviderObserver is a widget in the Riverpod state management library that allows you to observe changes to a provider, and react to them accordingly.

Here’s an example code snippet that shows how to use ProviderObserver to display a counter:

you can create this with a separate class as well, for just understanding purposes I write all this in one page, basically, here we use a StateProvider to create a provider that holds an integer. We then use a ConsumerWidget to build a widget that displays the value of the provider. We use ProviderScope to override the state of the provider with a new value of 42 and then use ref.watch(myProvider) to listen to changes to the provider's state. We use ScopedProviderRef to access the state of the provider within the context of the ProviderScope. We also use the .autoDispose modifier to ensure that the overridden provider is automatically disposed of when it is no longer needed.

That’s all the information I wanted to share about Riverpod. In comparison with Provider, Riverpod offers a better solution for managing the state in Flutter. As a result, it addresses the limitations of Provider and enables a robust architecture for your Flutter application, enabling you to build a reliable application from the ground up. In addition, I obtained all the information from a variety of websites as part of some research, so if you find any errors or misdirected links, please let me know.

If you got something wrong? Mention it in the comments. I would love to improve. your support means a lot to me! If you enjoy the content, I’d be grateful if you could consider subscribing to my YouTube channel as well.

I am Shirsh Shukla, a creative Developer, and a Technology lover. You can find me on LinkedIn or maybe follow me on Twitter or just walk over my portfolio for more details. And of course, you can follow me on GitHub as well.

Have a nice day!🙂

--

--

Shirsh Shukla

SDE at Reliance Jio | Mobile Application Developer | Speaker | Technical Writer | community member at Stack Overflow | Organizer @FlutterIndore