
Riverpod is the response to all the insufficiencies of other dependency injection and state management packages for Dart & Flutter apps. It's quick to get familiar with, maintainable, testable and it's much less error-prone than the other solutions out there. Let's take a look at the core principles of Riverpod.
How is Riverpod any different?
There are multiple ways to provide dependencies around your Flutter app. Service locators, such as get_it, are great but they aren't directional, for lack of a better word. This means you can access objects in whatever order you want and you're not necessarily guaranteed that all of them are properly initialized unless you're careful. This can result in major headaches especially on large projects with huge amounts of dependencies.

Another solution is to use the Provider package which is a well-known and widely accepted package for providing objects around your Flutter apps. In this tutorial I assume that you've had a chance to use it before. It's great, it's directional but it also has many drawbacks.
Provider is not perfect

For one, Provider is dependent on Flutter since you're using widgets to provide objects down the widget tree. I've never been very comfortable with mixing the UI code with dependency injection. Provider's usage of widgets also goes hand in hand with all the nesting it creates. Imagine we have MySecondClass
that depends on MyFirstClass
.
main.dart
class MySecondClass {
final MyFirstClass myFirstClass;
MySecondClass(this.myFirstClass);
}
Although I'm not going to explain the Provider package in this tutorial, take a look at the horribly nested UI code needed to properly construct an instance of MySecondClass
.
main.dart
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider(
create: (context) => MyFirstClass(),
child: ProxyProvider<MyFirstClass, MySecondClass>(
update: (context, firstClass, previous) => MySecondClass(firstClass),
child: MyVisibleWidget(),
),
);
}
}
Secondly, Provider relies only on types to resolve the requested object. If you provide two objects of the same type, you can get only the one closer to the call site.
main.dart
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider(
create: (context) => 'A String far away.',
child: Provider(
create: (context) => 'A String that is close.',
builder: (context, child) {
// Displays 'A String that is close.'
// There's no way to obtain 'A String far away.'
return Text(Provider.of<String>(context));
},
),
);
}
}
Lastly, if you try to access a type which is not provided at all, you're going to get an error only at run-time. This is not ideal since we should always strive to catch as many errors as possible at compile-time.
The basics of Riverpod

Riverpod combines the best things from service locators and Provider which results in a package that is just pleasurable to use . It's compile-time safe, doesn't depend on Flutter, it's still directional and the boilerplate is minimal. Sounds too good to be true? Well, this old adage just doesn't apply in some cases and Riverpod is thankfully one of them.
The first step is to add the package as a dependency to your Flutter project. In this tutorial, we're going to use flutter_riverpod which is the way to go if you want to use pure Flutter.
pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^0.12.1
If you're using flutter_hooks in your project, you will probably want to also use hooks_riverpod. Lastly, there's the pure Dart riverpod package in case you're developing for something else than the Flutter framework.
Riverpod's Providers aren't placed into the widget tree. They're instead put into global variables which can be inside any file you want. As long they're accessible, you're fine.
main.dart
final greetingProvider = Provider((ref) => 'Hello Riverpod!');
This simplest Provider
can expose a read-only value. There are many more types of Providers for working with Futures, Streams, ChangeNotifiers, StateNotifiers and more.
ref
parameter is of type ProviderReference
. As you'll see later on, it's mostly used to resolve dependencies between providers.While the Provider
object is globally accessible, this does not mean that the provided object (in this case a string "Hello Riverpod!") is itself global. Much like with a global function, you can call it from anywhere but the returned value may also be scoped locally. Consider the following code:
function_analogy.dart
String globalFunction() {
return 'some value';
}
class MyClass {
void _classMethod() {
final valueLocalToThisMethod = globalFunction();
}
}
Where are the widgets!?
Although it's great news that Riverpod's Providers are Flutter-independent, we still need to use the value provided by a Provider
object from the widget tree - this is Flutter, after all.
The Riverpod package comes with just a single InheritedWidget
that needs to be placed above the whole widget tree called ProviderScope
. It's responsible for holding something called a ProviderContainer
which is in turn responsible for storing the state of individual Provider
objects.
main.dart
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
ProviderScope
wrapping the whole widget tree is enough. You can have multiple ones if you want to override certain providers for a part of the widget tree.Watching a provider

How do we then obtain the string from the greetingProvider
so that we can display it using a Text
widget? There are actually two ways to do it. Let's showcase both based on the same piece of standard Flutter code. We want to get the provided string into the highlighted widget.
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod Tutorial',
home: Scaffold(
appBar: AppBar(
title: Text('Riverpod Tutorial'),
),
body: Center(
child: Text('greeting goes here'),
),
),
);
}
}
The first way is to change the superclass of our widget to ConsumerWidget
which comes from the flutter_riverpod package. This adds a ScopedReader
function into the widget's build
method. Since we use the ScopedReader
to watch the provider and rebuild the widget if any change occurs, the convention is to give this parameter a name "watch".
main.dart
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
// Gets the string from the provider and causes
// the widget to rebuild when the value changes.
final greeting = watch(greetingProvider);
return MaterialApp(
title: 'Riverpod Tutorial',
home: Scaffold(
appBar: AppBar(
title: Text('Riverpod Tutorial'),
),
body: Center(
child: Text(greeting),
),
),
);
}
}
The other way of getting the value out of a provider is useful if you want to quickly optimize your widget rebuilds. It's not the end of the world in our case, but we're still rebuilding the whole MaterialApp
while, in fact, we only need to rebuild the affected Text
widget all the way down the widget tree. This is what the Consumer
widget is for.
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod Tutorial',
home: Scaffold(
appBar: AppBar(
title: Text('Riverpod Tutorial'),
),
body: Center(
child: Consumer(
builder: (context, watch, child) {
final greeting = watch(greetingProvider);
return Text(greeting);
},
),
),
),
);
}
}
Reading a provider
Sometimes, it may be impossible to call watch
because you're not inside of the build
method. For example, you may want to perform an action when a button is pressed. That's when you can call context.read()
. Let's showcase it on another type of a provider - ChangeNotifierProvider
. Consider this code:
main.dart
class IncrementNotifier extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value += 1;
notifyListeners();
}
}
final incrementProvider = ChangeNotifierProvider((ref) => IncrementNotifier());
As you've probably seen a thousand times already, we're going to create the classic counter app. A Text
widget will watch the provider and get automatically rebuilt if a change occurs and the FloatingActionButton
will only read the provider to call the increment()
method on it.
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod Tutorial',
home: Scaffold(
body: Center(
child: Consumer(
builder: (context, watch, child) {
final incrementNotifier = watch(incrementProvider);
return Text(incrementNotifier.value.toString());
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(incrementProvider).increment();
},
child: Icon(Icons.add),
),
),
);
}
}
Stuff you can't do with the "original" Provider
As you could've seen by now, Riverpod's providers objects don't rely on types for resolving their values. This is in contrast with the "original" Provider package. This is awesome because you can have multiple providers of the same type without any issue.
example.dart
final firstStringProvider = Provider((ref) => 'First');
final secondStringProvider = Provider((ref) => 'Second');
// Somewhere inside a ConsumerWidget
final first = watch(firstStringProvider);
final second = watch(secondStringProvider);
Another conclusion that logically flows out of Riverpod not relying on types is that you cannot possibly try to get a value which is unavailable. You can only watch
or read
a provider variable that you can access and as long as you can access a provider, its value has to be available, right?
Dependency between providers
Any real-world app has dependencies between classes. For example, you can have a ChangeNotifier
that depends on a Repository
which in turn depends on a HttpClient
. Handling such dependencies with Riverpod is simple and readable.
To keep our example simple, let's just have a FutureProvider
depend directly on a simple FakeHttpClient
. Getting hold of another provider inside a provider's function is done by calling read
on the ProviderReference
parameter which is always passed in. If you depend on a provider whose value can change, you can also call watch
.
FutureProvider
but I don't think its creation requires much explanation. It looks almost exactly like the most basic Provider
but the function that you pass in returns a Future
. Just like with aProvider
the creation function of a FutureProvider
runs as soon as we obtain it from a widget for the first time.main.dart
class FakeHttpClient {
Future<String> get(String url) async {
await Future.delayed(const Duration(seconds: 1));
return 'Response from $url';
}
}
final fakeHttpClientProvider = Provider((ref) => FakeHttpClient());
final responseProvider = FutureProvider<String>((ref) async {
final httpClient = ref.read(fakeHttpClientProvider);
return httpClient.get('https://resocoder.com');
});
Using values from a FutureProvider
from the UI is absolutely delightful. Gone are the days of clumsy FutureBuilder
s! Riverpod makes building widgets based on a Future
easy. It uses freezed unions behind the scenes to break down a Future
object into an AsyncValue
union which we can easily map to different widgets.
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod Tutorial',
home: Scaffold(
body: Center(
child: Consumer(
builder: (context, watch, child) {
final responseAsyncValue = watch(responseProvider);
return responseAsyncValue.map(
data: (_) => Text(_.value),
loading: (_) => CircularProgressIndicator(),
error: (_) => Text(
_.error.toString(),
style: TextStyle(color: Colors.red),
),
);
},
),
),
),
);
}
}
Passing arguments to providers

What if we wanted to pass a user-defined URL to the responseProvider
? That's what provider families are for! By changing the responseProvider
to the following...
main.dart
final responseProvider =
FutureProvider.family<String, String>((ref, url) async {
final httpClient = ref.read(fakeHttpClientProvider);
return httpClient.get(url);
});
...we can now pass the request URL from the UI. Sure, there's no actual user input to keep things simple but you can try changing the hard-coded URL string and you'll see that the FutureProvider
reruns its creation function each time you change the passed in string.
main.dart
final responseAsyncValue = watch(responseProvider('https://resocoder.com'));
Automatically disposing of state
Here's an interesting observation - try changing the inputted URL to A then to B and lastly back to A. What happened? When you changed the value back to A, there was no CircularProgressIndicator
displayed which means that the value was readily present in the FutureProvider
and there was no need to perform yet another fake get request.
This caching of a provider's state is great but sometimes you may want to perform requests even to the same URL over and over again. That's what the autoDispose
modifier is for.
main.dart
final responseProvider =
FutureProvider.autoDispose.family<String, String>((ref, url) async {
final httpClient = ref.read(fakeHttpClientProvider);
return httpClient.get(url);
});
This modifier will dispose of the provider's state as soon as the provider is not used. In our case, this happens if we change the argument passed into the provider family. However, autoDispose
is useful even if you're not using a family
modifier. In such case, disposal will be the started when the ConsumerWidget
that watches a provider is disposed.
Conclusion
You've just learned the essence of the Riverpod package. We've gone through the very basics all the way to provider modifiers. Of course, we can go more in-depth because this package has a lot more to offer. If you don't want to miss the upcoming tutorials about building more advanced apps with Riverpod, sign up to the weekly Flutter newsletter below.
When I access this page, YouTube throws an error: Youtube refuses to connect.
I heard this can replace get_it, however I still don’t get clearly how to use it to inject/locate repository and other things.
So if I have these:
– Repository Class which implements IRepository
– FakeHttpClient Class which implement IHttpClient
– FirebaseLogger which implement ILogger
– Repository that use IHttpClient and ILogger
– Provider Class that extend ChangeNotifier and use IRepository
then I have to create:
final httpClientProvider = Provider((ref) => FakeHttpClient());
final loggerProvider = Provider((ref) => FirebaseLogger());
final repositoryProvider = Provider((ref) => Repository(ref.read(httpClientProvider)));
final provider = ChangeNotifierProvider(
(ref) => Provider(
ref.read(loggerProvider),
ref.read(approvalRepository),
));
Is this right?
Yes, that’s right. As someone pointed out on YouTube, you should only use `ref.watch` inside providers.
I have an abstract class LectureRepository implemented by LectureRepositoryImpl. LectureRepositoryImpl initialises SharedPreferences sharedPreferences with
LectureLocalDataSourceImpl({@required this.sharedPreferences})
Now I want to use Riverpod to access the repository so I do:
FutureProvider(
(ref) async {
return LectureLocalDataSourceImpl(sharedPreferences: await SharedPreferences.getInstance());
},
);
but now in lectureRepositoryProvider, wenn I read the lectureLocalDataRepositoryProvider I get an AsyncValue in the value localDataSource, which I cannot assign here:
final lectureRepositoryProvider = FutureProvider((ref) async {
final localDataSource = ref.read(lectureLocalDataRepositoryProvider);
return LectureRepositoryImpl(localDataSource: localDataSource);
});
How should I handle the AsyncValue?
Great video and very instructive. Please take a look at my SO question here: https://stackoverflow.com/questions/66233832/statenotifierprovider-not-keeping-state-between-flutter-hot-restarts. What am I doing wrong?
Excellent article! I’d like to see it one day as a part of the official Riverpod documentation in Getting Started section.
This is one of the best tutorial on Riverpod out there.
Its like you read my mind! You appear to know so much about this, like you wrote the book in it or something. I think that you can do with a few pics to drive the message home a little bit, but other than that, this is fantastic blog. A great read. I’ll certainly be back.
Fantastic site. A lot of helpful info here. I’m sending it to some buddies ans additionally sharing in delicious. And naturally, thanks on your sweat!
Hi, Neat post. There’s an issue together with your web site in internet explorer, may test this텶E still is the marketplace chief and a good component of people will pass over your fantastic writing due to this problem.
My brother recommended I might like this web site. He was totally right. This post actually made my day. You cann’t imagine just how much time I had spent for this information! Thanks!
I loved as much as you’ll receive carried out right here. The sketch is tasteful, your authored material stylish. nonetheless, you command get bought an nervousness over that you wish be delivering the following. unwell unquestionably come more formerly again since exactly the same nearly a lot often inside case you shield this hike.
I was suggested this web site by my cousin. I’m not sure whether this post is written by him as no one else know such detailed about my trouble. You are incredible! Thanks!
Hello, Neat post. There’s an issue together with your site in internet explorer, would check this텶E still is the marketplace chief and a large element of other folks will leave out your magnificent writing due to this problem.
Wow, amazing blog layout! How long have you been blogging for? you made blogging look easy. The overall look of your web site is magnificent, as well as the content!
Normally I do not read article on blogs, however I would like to say that this write-up very forced me to try and do so! Your writing style has been amazed me. Thanks, quite great post.
I’ve read several just right stuff here. Certainly price bookmarking for revisiting. I wonder how a lot effort you place to create this kind of great informative website.
I do believe all the ideas you’ve presented for your post. They are really convincing and will certainly work. Nonetheless, the posts are too short for novices. May just you please lengthen them a little from subsequent time? Thanks for the post.
I have read some excellent stuff here. Definitely value bookmarking for revisiting. I wonder how much effort you put to make the sort of excellent informative website.
Somebody essentially lend a hand to make significantly articles I’d state. That is the very first time I frequented your website page and up to now? I surprised with the research you made to make this actual submit amazing. Wonderful task!
I loved as much as you will receive carried out right here. The sketch is tasteful, your authored subject matter stylish. nonetheless, you command get got an edginess over that you wish be delivering the following. unwell unquestionably come further formerly again as exactly the same nearly very often inside case you shield this hike.
I do not even know how I ended up here, but I thought this post was great. I do not know who you are but certainly you’re going to a famous blogger if you are not already 😉 Cheers!
Bwer Pipes: The Ultimate Destination for Irrigation Excellence in Iraq: Elevate your farm’s irrigation capabilities with Bwer Pipes’ premium-quality products. Our innovative sprinkler systems and robust pipes are engineered for durability and efficiency, making them the ideal choice for Iraqi farmers striving for success. Learn More
Bwer Pipes: Leading the Way in Agricultural Irrigation Technology: Revolutionize your irrigation practices with Bwer Pipes’ innovative solutions. Our cutting-edge sprinkler systems and durable pipes are engineered to withstand the harsh conditions of Iraqi agriculture, ensuring optimal water usage and crop growth. Learn More
Optimize Your Farm’s Water Management with Bwer Pipes: Bwer Pipes offers a comprehensive range of irrigation solutions designed to help Iraqi farmers maximize water efficiency. Our reliable sprinkler systems and durable pipes ensure uniform water distribution, promoting healthier crops and sustainable farming practices. Explore Bwer Pipes
Your point of view caught my eye and was very interesting. Thanks. I have a question for you.
Temp mail I’m often to blogging and i really appreciate your content. The article has actually peaks my interest. I’m going to bookmark your web site and maintain checking for brand spanking new information.
Ny weekly Nice post. I learn something totally new and challenging on websites
Real Estate There is definately a lot to find out about this subject. I like all the points you made
Real Estate I’m often to blogging and i really appreciate your content. The article has actually peaks my interest. I’m going to bookmark your web site and maintain checking for brand spanking new information.
Techarp Nice post. I learn something totally new and challenging on websites
LDPE Pipes in Iraq Elite Pipe Factory in Iraq offers a comprehensive range of LDPE pipes, which are valued for their flexibility, lightweight nature, and resistance to various chemicals. Our LDPE pipes are engineered to meet high standards of quality, ensuring reliable performance across various applications. Recognized as one of the best and most reliable pipe manufacturers in Iraq, Elite Pipe Factory is committed to delivering products that combine durability with performance. For more information on our LDPE pipes, visit elitepipeiraq.com.
HDPE Pipes in Iraq Elite Pipe Factory in Iraq excels in the production of HDPE pipes, which are known for their strength, durability, and resistance to impact and chemicals. Our HDPE pipes are engineered to meet the toughest standards, making them ideal for a wide range of applications, from water distribution to industrial uses. As one of the best and most reliable pipe manufacturers in Iraq, Elite Pipe Factory is dedicated to providing products that deliver outstanding performance and longevity. Discover more about our HDPE pipes and other offerings at elitepipeiraq.com.
Clay Pipes in Iraq At Elite Pipe Factory, we take pride in offering high-quality clay pipes, a trusted solution for traditional and modern construction needs in Iraq. Our clay pipes are renowned for their durability, resistance to harsh environmental conditions, and their role in sustainable infrastructure projects. Elite Pipe Factory stands out as one of the best and most reliable manufacturers in Iraq, providing clay pipes that meet rigorous industry standards. For more information about our clay pipes and other products, visit our website at elitepipeiraq.com.
أنابيب الألياف الزجاجية والراتنج في العراق تفتخر شركة إيليت بايب في العراق بأنها منتج رائد لأنابيب الألياف الزجاجية والراتنج عالية الجودة، التي توفر أداءً ممتازًا ومتانة لتطبيقات صناعية متنوعة. توفر أنابيب البلاستيك المدعمة بالألياف الزجاجية (FRP)، والمعروفة أيضًا بأنابيب GRP، مقاومة ممتازة للتآكل، وخصائص خفيفة الوزن، وعمر خدمة طويل. تجعل هذه الخصائص منها مثالية للاستخدام في بيئات تتطلب أداءً عالياً مثل معالجة المواد الكيميائية، ومعالجة المياه، وصناعات النفط والغاز. مع التزامنا بالابتكار والجودة، تضمن شركة إيليت بايب أن كل أنبوب يلبي المعايير الصارمة، مما يثبت مكانتنا كواحدة من أفضل وأكثر الموردين موثوقية في العراق. لمزيد من المعلومات، تفضل بزيارة موقعنا على elitepipeiraq.com.
Henof Great information shared.. really enjoyed reading this post thank you author for sharing this post .. appreciated
Newtoki For the reason that the admin of this site is working, no uncertainty very quickly it will be renowned, due to its quality contents.
FinTech ZoomUs Good post! We will be linking to this particularly great post on our site. Keep up the great writing
Noodlemagazine I’m often to blogging and i really appreciate your content. The article has actually peaks my interest. I’m going to bookmark your web site and maintain checking for brand spanking new information.
NY weekly Very well presented. Every quote was awesome and thanks for sharing the content. Keep sharing and keep motivating others.
Noodlemagazine I really like reading through a post that can make men and women think. Also, thank you for allowing me to comment!
Noodlemagazine Good post! We will be linking to this partspacelarly great post on our site. Keep up the great writing
Technology us very informative articles or reviews at this time.