— Flutter, State Management, Dart, Riverpod — 2 min read
I wrote a quick tutorial a couple of days ago that showed how to handle the state of a counter with Riverpod. In that post I only really briefly wrote about ProviderReference so today I'm writing another short tutorial about merging two Providers, using the the last tutorial as a base. If you haven't viewed the last tutorial you should probably read it first, you can check it out here.
First of all there were a couple of packages that needed to be added to pubspec.yaml in the last tutorial. They were:
In the last tutorial we had a class called CounterState
to hold the counter's state:
1// To create a StateNotifier you first need to create class/model to hold the state2class CounterState {3
4 CounterState({this.value = 0});5 final int value;6
7 // This is just a simple utility method, you might want to try out freezed8 // for more complex implementations9 CounterState copyWith({int count}) {10 return CounterState(11 value: count ?? this.value12 );13 }14
15}
We also created a StateNotifier, CounterNotifier
, to handle updates for the above class:
1class CounterNotifier extends StateNotifier<CounterState> {2 CounterNotifier() : super(CounterState());3
4 increase() => state = state.copyWith(count: state.value + 1);5 decrease() => state = state.copyWith(count: state.value - 1);6}7
8// Adding .autoDispose will dispose of the provider when it is no longer needed9final counterProvider = StateNotifierProvider.autoDispose((_) => CounterNotifier());
We then finally created a widget called CounterPage
that showed the counter's value and increase and decrease buttons.
In the last tutorial we only actually had one Provider, and that was counterProvider
. This time I'm writing about merging Providers so we're going to create a StateProvider that will hold an int value(a multiplier to be more specific).
1// A StateProvider for an int value2final multiplierProvider = StateProvider.autoDispose((_) => 4);
No changes need to be made to counterProvider
, it is fine as it is already. Next we're going to create a Provider the will merge two values together: the value of the counter and the multiplier. Like I said in the last tutorial, when you create Provider you will always have something called a ProviderReference
, you can use this reference to link to other existing Providers.
So here's the last Provider we're going to create:
1// A Provider that merges both of the above providers2final valueProvider = Provider.autoDispose((ref) {3 // Watch the value of the original counter4 final value = ref.watch(counterProvider.state).value;5 // Watch the value of the multiplier6 final multiplier = ref.watch(multiplierProvider).state;7 // Multiply the original value by the multiplier8 return value * multiplier;9});
The above provider generates an int value again. The ref
variable above is a ProviderReference
which has a few useful methods, the most useful are probably read
and watch
. In this case, we're watching the value of the counter and then the value of the multiplier. We then return the original value multiplied by the multiplier. We're using ref.watch
instead of read.read
in this case because you don't get updates if you use read
.
In most cases you will want to use watch
unless you're hooking into something that will never change like maybe a repository class that you wrapped in a Provider purely for dependency injection purposes.
All that's left to do is consume the new Provider in a HookWidget. I didn't really have to change much at all from the last tutorial, here's the new HookWidget:
1lass CounterPage extends HookWidget {2
3 @override4 Widget build(BuildContext context) {5 final int value = useProvider(valueProvider);6 final CounterNotifier counterNotifier = useProvider(counterProvider);7 return Scaffold(8 appBar: AppBar(9 title: Text('CounterPage'),10 ),11 body: Container(12 width: double.infinity,13 child: Column(14 mainAxisAlignment: MainAxisAlignment.center,15 crossAxisAlignment: CrossAxisAlignment.center,16 children: [17 Padding(18 padding: EdgeInsets.all(10),19 child: Text('Count: $value')20 ),21 ElevatedButton(22 child: Text('Increase'),23 onPressed: () => counterNotifier.increase(),24 ),25 ElevatedButton(26 child: Text('Decrease'),27 onPressed: () => counterNotifier.decrease(),28 )29 ],30 ),31 ),32 );33 }34
35}
As you can see we're no longer listening to the state of CounterNotifier anymore. Now we can just listen to valueProvider
directly to get the (multiplied) value of the counter. Now when you tap "Increase" or "Decrease" the counter value should move in increments of 4.
I hope this tutorial was helpful if you were looking for a simple example of merging two Providers together with Riverpod. I didn't really get to explain the differences between Provider, StateProvider or StateNotifierProvider. Just like with the Provider package, there's a lot of different types of Providers. I guess I will have to leave that for another post in the future. Anyway, if you want to see the full code of what I wrote above you can see a gist of it here.