Flutter Riverpod — The complete crash course
· Basics — What is a provider?
· Basics — Create a provider
· Basics — Reading a provider
∘ Obtain from a provider
∘ Obtain from a widget
∘ Different ways of reading a provider
· Different types of providers
· Further reading and Conclusion
Riverpod is one of the fastest growing and most liked state management solutions that exist. That’s why I will show you in this article, how to use Riverpod to build your first app with it. Let’s get started!
Let’s start on how to install Riverpod. There are three main packages, that you can install:
- Flutter only — flutter_riverpod — The basic way of using Riverpod with Flutter
- Flutter + flutter_hooks — hooks_riverpod — A way to use both flutter_hooks and Riverpod together. You can find a crash course on flutter_hooks here.
- Dart only — riverpod — A version of Riverpod with all the classes related to Flutter stripped out
If you want to go further, you can install snippets in VSCode:
Basics — What is a provider?
Providers are maybe the most important part of Riverpod. Everything you do with Riverpod is based somehow on a provider. That’s why it’s very important to understand what a provider is. As everything in Dart, and so in Riverpod, it’s an object which encapsulates a piece of state but also allows one to listen to that state and access it. There are many advantages in wrapping a piece of state in a provider:
- Allows you to easily access that state in multiple locations — Providers in Riverpod are fully Flutter-independent
- Very easy to combine multiple states
- Great performance optimizations — Only what is impacted by a state is recomputed
- Great testability — No complex setUp/tearDown steps and can be overridden in tests
- Easy integration with advanced features (logging, pull-to-refresh)
As you can see, providers can be very useful. But first, we have to create a provider to use it:
Basics — Create a provider
There are 6 different types of providers, but we will just focus on the most basic one for now. The others I will explain at the end very quickly. The most basic one is a provider. It’s very easy to declare a provider as a global constant. But don’t worry, Providers are fully immutable.
If you just use dart, the example above works completely fine. To use Flutter with providers, you need to wrap the Widget
ProviderScope at the root of your application.
Providers also have modifiers, that add new features to the
ref object or change slightly how the provider is consumed.
.autoDispose- The provider will destroy its state when it’s no longer being listened to.
w.family- Allows creating a provider from external parameters
It can even use multiple modifiers at once:
Basics — Reading a provider
I think this is one of the most important parts of this article. I mean, what is the sense of having a provider without reading from it?
Okay, to read a Provider, you always need a “ref” object. You can obtain it in two different ways: Obtaining from a provider and Obtaining from a widget. Let’s start with the provider:
Obtain from a provider
As already said in the previous chapter, every provider receives a “ref” parameter. You can use this to watch other providers:
A very common way to use this is StateNotifier, but we will discuss this later.
Obtain from a widget
To use providers in a Flutter Widget, Riverpod has created its widgets for that. ConsumerWidget (replacement for StatelessWidget) and ConsumerStatefulWidget (replacement for StatefulWidget).
In a ConsumerWidget, the build method has an extra parameter: ref, which is a
When using a
ConsumerStatefulWidget, you have to use
ConsumerStatefulWidget instead of
ConsumerState instead of
As you can see, we don’t pass
ref as a parameter. That’s because it’s already built in as a property in
For flutter hooks, you just replace
HookWidget them with a
HookConsumerWidget. Pass a ref to build method and you are done. If you don’t know Flutter Hooks yet, then you can learn it here.
If you want to use StatefulWidget with flutter_hooks and riverpod, then
StatefulHookConsumerWidget is helpful for you. You still use
ConsumerState, but this time instead
ConsumerStatefulWidget you use
Different ways of reading a provider
Now we know, how to obtain ref in different ways. Now let’s take a look at the different ways we can read the provider. Explaining every way to read would be way too long for this article. That’s why I will summarize it very quick and provide a link to read more about these things:
- ref.watch to observe a provider
◦ Used inside of build method or inside the body of a provider to listen to a provider
◦ Can be used to combine providers:
◦ Caution: Don’t call watch asynchronously or inside State life-cycles (like initState) → Use ref.read instead
- ref.listen to react to a provider change
◦ Like ref.watch, but when the provider state changes, you can call a function
◦ Needs 2 positional arguments: Provider and a callback function to execute when state changes
- ref.read to obtain the state of a provider
◦ Obtain the current state of a provider
◦ Does not react to changes (doesn’t listen to provider)
◦ Should be avoided as much as possible, because it’s not reactive → Exists for cases where
listencause errors (like initState)
◦ Don’t use inside build method of a widget
◦ Used to trigger a function by a user interaction:
- “select” to filter rebuilds
◦ Example: userProvider with an email and a username
◦ Only want to listen to the username if it changes, but widget would rebuild when the email changes
Different types of providers
There are different types of providers that you can use in different cases. I want to explain them as short as possible. If you want to read more about the specific provider, I will provide a link to a more detailed explanation.
The provider is the most basic way of all providers.
It’s used for:
- cache computations
- expose a value to other providers
- offer a way for tests or widgets to override value
- reduce rebuilds of providers/widgets without having to use
StateNotifierProvider is used to listen to and expose a StateNotifier (from package state_notifier)
It’s used for:
- Expose an immutable state that can change over time after reacting to custom events
- Centralize the logic for modifying some states in a single place, improving maintainability over time
The equivalent of Provider, but for asynchronous code
It’s used for:
- perform and cache asynchronous operations
- nicely handle error/loading of asynchronous operations
- combine multiple asynchronous values into another value
You can see, that it’s used for the same things as a provider, but everything for asynchronous code.
Similar to FutureProvider, but for Streams instead of Futures
It’s used for:
- Listen to firebase or web-sockets
- Rebuild another provider every few seconds/minutes etc.
A simplification of StateNotifierProvider that exposes a way to modify its state. It was designed to avoid having to write a StateNotifier class for simple use cases. That’s why it’s often one of the following:
- enum (e.g. filter type)
- String (e.g. raw content of a text field)
- boolean (e.g. for checkboxes)
- number (e.g. pagination or age form fields)
Don’t use for:
- Your state needs validation logic
- a state is a complex object (custom class, list/map, …)
- logic for modifying state is more advanced than simple count++
It’s used to listen to and expose a ChangeNotifier from Flutter itself. Discouraged by Riverpod any exists primarily for:
- An easy transition form provider package (if you are using its
- Supports mutable state, even though immutable state is preferred
It’s preferred to use StateNotifierProvider instead. Only use ChangeNotifierProvider if you are absolutely certain that you want mutable state.
Further reading and Conclusion
In this article, you have learned the basics of the state management solution Riverpod. You have seen how easy it is to use and how much time it saves you.
You can unfold the whole power of Riverpod if you use packages like Freezed, Isar or Flutter Hooks.
In the next few articles, I will introduce more somewhat complicated packages and explain them. If you don’t want to miss this, I recommend you to follow me.
I tried my best to write the easiest tutorial which everyone understands. If you appreciate this work, I would be very grateful if you could support this quality content and give me some claps!
Thanks for reading, I wish you a great day and happy coding!
Final note: The whole article is inspired by the riverpod.dev documentation. Nearly all the code examples are from the websites and this article also follows the structure of the documentation. This article is intended to be an overview of Riverpod, where you can find everything important.