Why Riverpod?
What is Riverpod?
Riverpod (anagram of Provider) is a reactive caching framework for Flutter/Dart.
Using declarative and reactive programming, Riverpod takes care of a large part of your application's logic for you. It can perform network-requests with built-in error handling and caching, while automatically re-fetching data when necessary.
Motivation
Modern applications rarely come with all the information necessary to render their User Interface. Instead, the data is often fetched asynchronously from a server.
The problem is, working with asynchronous code is hard. Although Flutter comes with some way to create state variables and refresh the UI on change, it is still fairly limited. A number of challenges remain unsolved:
- Asynchronous requests need to be cached locally, as it would be unreasonable to re-execute them whenever the UI updates.
- Since we have a cache, our cache could get out of date if we're not careful.
- We also need to handle errors and loading states
Nailing those problems at scale can be difficult, and they are impacted by a large amount of features, such as:
- Pull to refresh
- Infinite lists / fetch as we scroll
- Search as we type
- Debouncing asynchronous requests
- Cancelling asynchronous requests when no-longer used
- Optimistic UIs
- Offline mode
- etc.
These features can be tricky to implement, but are crucial for a good user experience.
Yet few packages try to tackle those problems directly, and a lot of the work
has to be done manually.
That's where Riverpod comes in.
Riverpod tries to solve those problems, by offering a new unique
way of writing business logic, inspired by Flutter widgets. In
many ways Riverpod is comparable to widgets, but for state.
Using this new approach, these complex features are mostly done by default. All that's left is to focus on your UI.
Skeptical? Here's an example. The following snippet is a simplification of the Pub.dev client application implemented using Riverpod.
// Fetches the list of packages from pub.dev
Future<List<Package>> fetchPackages(
FetchPackagesRef ref, {
required int page,
String search = '',
}) async {
final dio = Dio();
// Fetch an API. Here we're using package:dio, but we could use anything else.
final response = await dio.get<List<Object?>>(
'https://pub.dartlang.org/api/search?page=$page&q=${Uri.encodeQueryComponent(search)}',
);
// Decode the JSON response into a Dart class.
return response.data?.map(Package.fromJson).toList() ?? const [];
}
This snippet is all the business logic you need for a "search as we type" + "pull to refresh" + "infinite list", while handling error/loading states.