Skip to main content
Compile safe

Compile safe

No more ProviderNotFoundException or forgetting to handle loading states. Using Riverpod, if your code compiles, it works.

Provider, without its limitations

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

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.

 Inspect your state in the devtool