Riverpod describes itself as
A Reactive Caching and Data-binding Framework
(for Flutter). But what’s most interesting for me is how it simplifies dependency injection and reactivity by leveraging the idea of a Provider.
p r o v i d e r
Just take this example:
final apiClientProvider = Provider((ref) => ApiClient());
final counterProvider = StateProvider<int>((ref) => 0);
final repositoryProvider = Provider((ref) {
final api = ref.watch(apiClientProvider); // dependency injection
final count = ref.watch(counterProvider); // reactive dependency
return Repository(api: api, count: count);
});
And then read and update the state:
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(counterProvider);
return Column(
children: [
Text(value.toString()),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).state++;
},
child: const Text("Increment"),
),
],
);
}
}
I guess you’d have to develop an app with flutter and riverpod to feel why it’s such a cool idea.
And maybe develop some larger apps to see why not so much…
Which I have not, so let’s move on.
Our goal here is not to explore flutter design patters but to instead understand how this provider mechanism works. And the best way to learn is to implement it yourself.
type ProviderFun<T> = fn(&Container) -> T;
struct Provider<T> {
f: ProviderFun<T>,
}
ex.
static PROVIDER: Provider<f32> = Provider::new(|_| 42.0);
Our provider will be just a wrapper around a function that takes
a reference to some Container (like the
ref in the flutter example above) and returns some
value.
The Container is the one that will cache the
providers’ values.
To be continued…