Why Choose Riverpod? - Riverpod Extensions

Why Choose Riverpod? - Riverpod Extensions

This post is based off a presentation I did at Devfest 2023 in Cape Town on 23 Nov 2023. VIDEO | PRESENTATION


Previously we took a look at what Riverpod is, in this post we Weill take a look at the extension methods provided by Riverpod.


To help with controlling the global access to your states data, Riverpod provides 2 extension methods, namely autoDispose and family.

Autodispose

final counterProvider = Provider.autoDispose<int>((ref) => 0);

The autoDispose extension does very must what the name suggests, once the widget instantiating the Provider is unmounted, the Provider is removed along with all of it's data.

Family

The family extension, which took me a while to fully understand initially, is the real powerhouse here, it serves 2 purposes, the first is simply the ability to pass data into your Provider. The one caveat with that is that it only allows a single argument to be passed in, but it does at least support all data types, so if you needed multiple pieces of information, you could make use of a data class or possible even a Map.

final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
  return dio.get('http://my_api.dev/messages/$id');
});

A common, albeit simple, use case one would run into where this is handy would be when making of of the FutureProvider, one of Riverbeds providers for dealing with promises, such as network request.

As you can see above, we are passing in a String as the second argument to the provider, with the first argument being the expected response type from the API call.

Uniqueness

A big reason to make use of the family extension is to control unique versions of a state when needing to have access to the same logic with multiple widgets in the same view.

Screenshot of mobile application

Looking at this image you will see the 3 cards, all 3 of them make use of the same business logic, but would need to be able to uniquely track their data.

final sharedProvider = StateNotifierProvider
.family<SharedNotifier, SharedState, String>(
  (ref, orderId) => OperationsNotifier(ref, orderId),
);

class SharedNotifier extends StateNotifier<SharedState> {
  final Ref ref;
  final String orderId;

  SharedNotifier(this.ref, this.orderId) : super(SharedState.initial());
}

The above code is a simple example of how one would make use of shared logic while also maintaining unique instances and therefor data.

In order to maintain this uniqueness with the family extension, we need to ensure that the second argument passed in is something that is unitedly washable, the easiest of which is often a string, which can be anything you use to uniquely identify each instance, in this case I am making use of the orderId, which is the DB id for the order being returned as part of the API response.

The example is based off Loops driver application, a driver will have a list of 1 or more tasks on their device, which would be sharing the same business logic like the current status of the task, and will need to be able to perform all the same actions such as progressing the order/updating it's status.

The uniqueness we getting from the provider allows us to ensure they we can use a single state to expose all the business logic and ensure that each of them has access to their own specific state.


Next we will take a look at how Riverpod providers can be accessed within your UI or data classes.


I hope you found this interesting, and if you have any questions, comments, or improvements, feel free to drop a comment. Enjoy your development journey :D

Thanks for reading.