Flutter – Introduction to State Management Using Riverpod
Last Updated :
07 Sep, 2021
Riverpod is a Reactive State-Management and Dependency Injection framework, it uses different providers to let us access and listen to state changes across our app, it is built by Remi Rousselet. If you don’t know what is state then I’ll recommend you to read this article first as it will be a little hard for you to understand Riverpod without understanding the “state” itself.
What does Riverpod actually do?
Riverpod is a state management helper. It basically makes our state (in clear words, our variables’ values) accessible in all parts of the app, it puts our state at the top of the widget tree and lets us listen to those state changes and update our User Interface accordingly.
There are many types of providers which Riverpod offers, We’ll go over them one by one.
What are Providers?
Providers are the most important part of a Riverpod application, “A provider is an object that encapsulates a piece of state and allows listening to that state.”
Types of Providers:
- StateProvider
- FutureProvider
- StreamProvider
- Provider
Let’s start building!
1. Add Riverpod to our app:
Dart
dependencies:
flutter_riverpod: ^0.14.0+3
|
2. Wrapping our App:
For providers to work, we must add ProviderScope at the root of our Flutter applications:
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart' ;
void main() {
runApp(ProviderScope(child: MyApp()));
}
|
3. Create the providers.dart file:
We’ll define all our global providers in this file so that it is easier to maintain the project. Let’s define a StateProvider.
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart' ;
final userNameProvider=StateProvider<String>((ref) {
return "SomeName" ;
});
|
4. Reading Providers:
Now that we’ve declared a StateProvider, Let’s learn how can we read providers. To read any provider, we have multiple widgets, in this article, we’ll go over ConsumerWidget() and Consumer().
Using a ConsumerWidget():
A consumer widget is a widget that we can use in place of our Stateful/Stateless widget. It gives us the ability to read/change the states of providers & also listen to them.
Dart
import 'Providers/providers.dart' as providers.dart;
import 'package:flutter_riverpod/flutter_riverpod.dart' ;
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
String name = watch(providers.userNameProvider).state;
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Center(child: const Text( 'GFG 🙂 ' ))),
body: Center(
child: Text( '$name' ),
),
),
);
}
}
|
Output:

Well and good. So now, whenever the value of userNameProvider changes, the Text() will be updated accordingly. But how are we gonna update the value of userNameProvider?
5. Updating/Changing the value of a StateProvider:
To change the value of StateProvider, we need a StateController. So let’s create it.
First, let’s remove the variable “name”. ̶
S̶t̶r̶i̶n̶g̶ ̶n̶a̶m̶e̶ ̶=̶ ̶w̶a̶t̶c̶h̶(̶p̶r̶o̶v̶i̶d̶e̶r̶s̶.̶u̶s̶e̶r̶N̶a̶m̶e̶P̶r̶o̶v̶i̶d̶e̶r̶)̶.̶s̶t̶a̶t̶e̶;̶
//removed as with it we can only listen/get the state not change/mutate the state.
//let's use this StateController instead.
StateController<String> nameController = watch(providers.userNameProvider);
// now we can get / set the state using name.state.
For the sake of simplicity, let’s use a FloatingActionButton, and using it let’s try to mutate(change) the state of userNameProvider.
Dart
import 'Providers/providers.dart' as providers.dart;
class Home extends ConsumerWidget {
int n = 0;
@override
Widget build(BuildContext context, ScopedReader watch) {
StateController<String> nameController = watch(providers.userNameProvider);
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
n++;
nameController.state =
"New Name $n" ;
},
),
appBar: AppBar(title: Center(child: const Text( 'GFG 🙂 ' ))),
body: Center(
child: Text(
'${nameController.state}' ,
),
),
),
);
}
}
|
Output:

Now, when we press on the FloatingActionButton(), we’ll see that the name changes every time with a different number. So that’s it. Using this information we can easily use StateProviders, mutate their values and listen to it.
Now, Let’s go over asynchronous providers.
1. FutureProvider:
When we work with asynchronous code we often use some Future based APIs, let’s look into how you can handle Future events using Riverpod. To demonstrate, we will create a method which returns a Future.
Dart
class FutureClass {
Future< int > getData(String para) async {
await Future.delayed(Duration(seconds: 5));
return 25;
}
}
|
Creating a provider for FutureClass:
final futureClass = Provider((ref) => FutureClass());
Now, let’s create our FutureProvider,
Dart
final response = FutureProvider< int >((ref) async {
final client = ref.read(futureClass);
return client.getData( 'some text as a parameter' );
});
|
Now, we can use ConsumerWidget to listen to the state changes and update our UI accordingly.
Consuming a FutureProvider:
Apart from the ConsumerWidget(), we also have a Consumer() widget. The difference is that while ConsumerWidget rebuilds the entire screen when state changes, Consumer() rebuilds only it’s child, making sure we don’t run into performance issues.
Let’s use Consumer() widget to watch(listen) for state changes.
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text( "FutureProvider Demo" ),),
body: Center(
child: Column(
children: [
Consumer(
builder: (context, watch, child) {
final futureData = watch(response);
},
),
],
),
),
);
}
}
|
As a Future can have 3 states, i.e, completed, in progress & error, we have the ability to handle those states separately using the .map function.
Dart
Consumer(
builder: (context, watch, child) {
final futureData = watch(response);
return futureData.map(
,
data: (data) => Text( '${data.value}' ,)
,
loading: (_) => CircularProgressIndicator()
error: (message) => Text(message.error),
);
},
),
|
If we want to pass a variable/object to the provider as an argument, then we can do that like-
Dart
final response=
FutureProvider.autoDispose.family< int , String>((ref, i_am_a_param) async {
final client = ref.read(futureClass);
return client.getData(i_am_a_param);
});
|
Complete Code:
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text( "FutureProvider Demo" ),),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (context, watch, child) {
final futureData = watch(providers.response);
return futureData.map(
data: (data) => Text(
'${data.value}' ,
),
loading: (_) => Column(
children: [
CircularProgressIndicator(),
Text(
'Fetching data' ,
),
],
),
error: (message) =>
Text(message.error.toString()),
);
},
),
],
),
),
),
),
);
}
}
|
Output:

2. StreamProvider:
We often use streams in a flutter application, be it fetching data from Firestore or reading data from a file. Let’s learn how we can handle those streams with Riverpod.
Let’s declare a stream first,
final streamProvider = StreamProvider<int>((ref) {
return Stream.fromIterable([105, 50]);
//in production this could be a stream of documents from Firestore
});
Consuming StreamProvider:
This time, let’s use ConsumerWidget() to consume StreamProvider’s data, it is fairly similar to that of FutureProvider.
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text( "StreamProvider Demo" ),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (context, watch, child) {
final streamValue = watch(providers.streamProvider);
return streamValue.when(
data: (data) => Text(
'${data}' ,
),
loading: () => Column(
children: [
CircularProgressIndicator(),
Text(
'Fetching data' ,
),
],
),
error: (message, e) =>
Text(message.toString()),
);
},
),
],
),
),
),
),
);
}
}
|
Now, every time the stream has new data, our UI will update accordingly.

Notice how we didn’t have to manage different states by ourselves, without Riverpod, the above code would look something like this:
Dart
final stream = Stream.fromIterable([105, 50]);
StreamBuilder< int >(
stream: stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
return SomeWidget(snapshot.data);
} else if (snapshot.hasError) {
return SomeErrorWidget(snapshot.error);
} else {
return Text( 'No data' );
}
} else {
return CircularProgressIndicator();
}
}
)
|
That’s it. This should cover almost everything we need to learn to get started with Riverpod .
The complete code for the article above can be found here.
Similar Reads
State management using Redux in Flutter
State management is a crucial aspect of building dynamic and responsive applications. While building applications, we sometimes need to maintain a state between multiple screens. Here comes the part of State Management. There are different state management techniques in Flutter like GetX, Provider,
6 min read
Flutter - State Management Provider
In this article, we are going to learn how state management is achieved in Flutter using providers. But before that, we need to know what a state is. As we know that everything in Flutter is a widget, and there are mainly two kinds of widgets: Stateless Widgets and Stateful Widgets. Stateless widget
8 min read
How to Manage State in Flutter with BLoC Pattern?
State management is an essential part of building any app, and it becomes especially important as your app grows in complexity. In Flutter, there are a number of approaches that you can take to manage state, including using global variables, Inherited Widgets, and more recently, the Provider package
6 min read
Flutter - State Management
In a reactive framework like Flutter, you can think of your UI as the function's return value. The function here is considered as your state. So in simple words, what a user sees in the application is one state, and he clicks on the button, he sees another UI screen. So now that screen is a state. S
5 min read
Flutter - Navigation to Previous Screen using GetX Library Function
When we are using any app then we do navigation to navigate between screens. Sometimes, we want to return to the previous screen, so we normally use Navigator.pop(context). This is using context, and sometimes we find shortcuts to do the task easily. For that, we haveGet.back()in flutter. We can sen
5 min read
Flutter - GetX State Management Library
GetX is a fast, stable, and light state management library in Flutter that simplifies the process of managing and updating the state of your application. It is an alternative to other state management libraries in Flutter like MobX, BLoC, Redux, Provider, etc. GetX is also a powerful micro framework
5 min read
Flutter - Send Data to Screen using RouteSettings
Interaction with the UI is an integral part of any application. But more often than not, the information needs to be sent from one screen to another. For instance, say you need to pass data regarding a selected or tapped component of the UI to another route(i.e., page). In this article, we will expl
4 min read
Flutter - Handle Future Methods Using Future.then() Method
Handling the Future methods is am important part ,In Flutter, we can use the Future.then() method to perform an action when a Future completes its execution. In this we are going to create an button by clicking the button a Future Method will start executing for 4 seconds then after the execution th
4 min read
Flutter - Navigation to Next Screen using GetX Library Function
When we want to navigate between the screens in flutter then we use Navigator in Flutter. It uses context and builders for navigation. Sometimes, we pass the data to the next screen and fetch data on the next screen. When we use Navigator in flutter then its syntax is very large and it's not a good
3 min read
Flutter | An introduction to the open source SDK by Google
Flutter is Googleâs Mobile SDK to build native iOS and Android, Desktop (Windows, Linux, macOS), and Web apps from a single codebase. When building applications with Flutter, everything is Widgets â the blocks with which the flutter apps are built. They are structural elements that ship with a bunch
5 min read