Compile safe
No more ProviderNotFoundException
or forgetting to handle loading
states. Using Riverpod, if your code compiles, it works.
Provider, without its limitations
Riverpod is inspired by Provider but solves some of it's key issues such as supporting multiple providers of the same type; awaiting asynchronous providers; adding providers from anywhere, ...
Doesn't depend on Flutter
Create/share/tests providers, with no dependency on Flutter. This
includes being able to listen to providers without a
BuildContext
.
Declare shared state from anywhere
No need to jump between your main.dart
and your UI files
anymore.
Place the code of your shared state where it belongs, be
it in a separate package or right next to the Widget that
needs it, without losing testability.
// A shared state that can be accessed by multiple
// objects at the same time
final countProvider = StateProvider((ref) => 0);
// Comsumes the shared state and rebuild when it changes
class Title extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(countProvider).state;
return Text('$count');
}
}
Recompute states/rebuild UI only when needed
We no-longer have to sort/filter lists inside the build
method or have to resort to advanced cache mechanism.
With Provider
and "families", sort your lists or do HTTP
requests only when you truly need it.
final todosProvider = StateProvider<List<Todo>>((ref) => []);
final filterProvider = StateProvider<Filter>((ref) => Filter.all);
final filteredTodosProvider = Provider<List<Todo>>((ref) {
final todos = ref.watch(todosProvider.state);
switch (ref.watch(filterProvider)) {
case Filter.none:
return todos;
case Filter.completed:
return todos.where((todo) => todo.completed).toList();
case Filter.uncompleted:
return todos.where((todo) => !todo.completed).toList();
}
});
Safely read providers
Reading a provider will never result in a bad state. If you
can write the code needed to read a provider, you will obtain
a valid value.
This even applies to asynchronously loaded values. As opposed
to with provider, Riverpod allows cleanly handling
loading/error cases.
// Parse a file without having to deal with errors
final configurationsProvider = FutureProvider((ref) async {
final uri = Uri.parse('configs.json');
final json = await File.fromUri(uri).readAsString();
return Configurations.fromJson(json);
});
class Example extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final configs = ref.watch(configurationsProvider);
// Use Riverpod's built-in support
// for error/loading states using "when":
return configs.when(
loading: (_) => const CircularProgressIndicator(),
error: (err, stack, _) => Text('Error $err'),
data: (configs) => Text('data: ${configs.host}'),
);
}
}
Inspect your state in the devtool
Using Riverpod, your state is visible out of the box inside Flutter's devtool.
Futhermore, a full-blown state-inspector is in progress.
