Riverpod

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.

The name riverpod is formed by rearanging the letters of

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.

A Memoized Function

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…