Flutter Riverpod: A Guide to Provider
What is Provider? How to use it? What is ProviderScope? What is Consumer? What is `ref.watch`, `ref.listen`, and `ref.read`? When to use what?

Search for a command to run...
What is Provider? How to use it? What is ProviderScope? What is Consumer? What is `ref.watch`, `ref.listen`, and `ref.read`? When to use what?

Lessons from Code, Content, and Consistency

Reliability basics: Outbox pattern + why it matters

Introducing Kafka/Redpanda + move to event-driven workflow

Connect Orders β Inventory (first working service-to-service flow)

Inventory service + gRPC + proto contracts


I'm so glad you're here. I know it can be overwhelming when you first start learning about state management, and I want to help you get started as quickly as possible.
In this post, we'll start with an introduction to the concept of state management and Provider. In the upcoming blog, We'll then go over different types of Providers that Riverpod has to offer to us and will see how it works and how it makes our lives easier.
After that, we'll create a real-world example using Riverpod. This will help you get a better understanding of how all of these providers work together to make our lives easier.
So without further ado, Let's get started.

pubspec.yaml.dependencies:
flutter_riverpod:
flutter pub get in your terminal. And now you are ready to do. 


final myNumberProvider = Provider<int>((ref) => 100);
final.myNumberProvider inside my flutter application. Yoo hold up... you forgot to explain about ref parameter. 
void main() {
runApp(ProviderScope(child: MyApp()));
}
I always forgot to do this π and end up in an error like this: Bad state: No ProviderScope found
Consumer widget directly.ConsumerWidget instead of StatelessWidget
Consumer widget:final myValueProvider = Provider<int>((ref) {
return 100;
});
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer( // <==
builder: (context, ref, child) {
final value = ref.watch(myValueProvider);
return Text(value.toString());
},
),
);
}
}
Consumer widget. builder. which expose 3 values: context, ref (Explained in further reading), and child.myValueProvider in our case ) changes then, only the widget wrapped inside it ( Text() in our case ) will get re-build, but the rest of the widgets will remain the same.ConsumerWidgetStatlessWidget with the ConsumerWidger.final myValueProvider = Provider<int>((ref) {
return 100;
});
class HomePage extends ConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(myValueProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod'),
),
body: Center(
child: Text(
value.toString(),
style: Theme.of(context).textTheme.headline2,
),
),
);
}
}
ConsumerWidget is identical in use to StatelessWidget, with the only difference being that it has an extra parameter on its build method: the ref object.Remember, I told you just a few minutes ago that I will explain to you what Ref is. I think now it's time to talk about the ref object.

There are mainly 3 use cases of Ref.
ref.read
class HomePage extends ConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod'),
),
body: Center(
child: GestureDetector(
onTap: () {
ref.read(loginRepository.notifier).login();
},
child: Text(
value.toString(),
style: Theme.of(context).textTheme.headline2,
),
),
),
);
}
}
ref.watch
ref.watch in order to obtain/listen to that other provider. Here is a simple example:// first provider
final helloStringProvider = StateProvider<String>((ref) {
return 'Hello';
});
// second provider
final worldStringProvider = StateProvider<String>((ref) {
return 'World';
});
final helloWorldStringProvider = Provider<String>((ref) {
final hello = ref.watch(helloStringProvider); // obtaining the helloStringProvider value inside this provider.
final world= ref.watch(worldStringProvider); // obtaining the worldStringProvider value inside this provider.
return '$hello$world';
});
class HomePage extends ConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(helloWorldStringProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod'),
),
body: Center(
child: Text(
value,
style: Theme.of(context).textTheme.headline2,
),
),
);
}
}
Now here is what will happen, Whenever anything changes in the provider that you are watching ( i.e helloStringProvider and wordStringProvider in our case ) inside your current provider ( i.e helloWorldStringProvider ), It will rebuild the widget or provider that subscribed to the value (i.e Text(value) in our case ).
FYI: I've used
StateProviderin the above example. We will look at what it actually does in the upcoming articles. For the time being, only know that we can use it to update the state.
ref.listen
ref.listen and then perform an action such as navigating to a new page or showing a modal, showing a snack bar whenever that provider changes.ref.watch and ref.listen is that, rather than rebuilding the widget/provider if the listened-to provider changes, using ref.listen will instead perform some operation/ call a function.final numberProvider = StateProvider<num>((ref) {
return 1;
});
class HomePage extends ConsumerWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(numberProvider);
ref.listen(helloStringProvider, (previousValue, newValue) {
log('Number Changed: $newValue');
});
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod'),
),
body: Center(
child: Text(
value.toString(),
style: Theme.of(context).textTheme.headline2,
),
),
);
}
}
read, watch and listen?ref.read: ref.read usually used in the cases like, on button press, getting the value from other provider, etc.ref.read should be avoided as much as possible because it is not reactive. It exists for cases where using watch or listen would cause issues.ref.watch:ref.watch over ref.read or ref.listen to implement a feature. By relying on ref.watch, your application becomes both reactive and declarative, which makes it more maintainable.ref.watch method should not be called asynchronously, like inside an onPressed of an ElevatedButton. Nor should it be used inside initState and other State life-cycles.ref.listen:ref.listen is usually used in cases where you want to perform something when any state changes. For example, Open a snack bar, Navigate to another screen, etc.ref.listen should not be called asynchronously, like inside an onPressed of an ElevatedButton. Nor should it be used inside initState and other State life cycles.