Loading...
Loading...
Uses Riverpod for state management in Flutter/Dart. Use when setting up providers, combining requests, managing state disposal, passing arguments, performing side effects, testing providers, or applying Riverpod best practices.
npx skill4agent add evanca/flutter-ai-rules riverpodvoid main() {
runApp(const ProviderScope(child: MyApp()));
}ProviderScoperunAppMyAppriverpod_lint// Functional provider (codegen)
int example(Ref ref) => 0;
// FutureProvider (codegen)
Future<List<Todo>> todos(Ref ref) async {
return ref.watch(repositoryProvider).fetchTodos();
}
// Notifier (codegen)
class TodosNotifier extends _$TodosNotifier {
Future<List<Todo>> build() async {
return ref.watch(repositoryProvider).fetchTodos();
}
Future<void> addTodo(Todo todo) async { ... }
}finalProviderFutureProviderStreamProviderConsumerWidgetConsumerStatefulWidgetStatelessWidgetStatefulWidget| Method | Use for |
|---|---|
| Reactively listen — rebuilds when value changes. Use during build phase only. |
| One-time access — use in callbacks/Notifier methods, not in build. |
| Imperative subscription — prefer |
| Cleanup when provider state is destroyed. |
// In a widget
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(myProvider);
return Text('$value');
}
}
// Cleanup in a provider
final provider = StreamProvider<int>((ref) {
final controller = StreamController<int>();
ref.onDispose(controller.close);
return controller.stream;
});ref.watchref.read(yourNotifierProvider.notifier).method()context.mountedrefawait
Future<String> userGreeting(Ref ref) async {
final user = await ref.watch(userProvider.future);
return 'Hello, ${user.name}!';
}ref.watch(asyncProvider.future)
Future<Todo> todo(Ref ref, String id) async {
return ref.watch(repositoryProvider).fetchTodo(id);
}
// Usage
final todo = ref.watch(todoProvider('some-id'));autoDisposeDart 3 records==ListMap==constprovider_parametersriverpod_lintkeepAlive: true.autoDispose// keepAlive with timer
ref.onCancel(() {
final link = ref.keepAlive();
Timer(const Duration(minutes: 5), link.close);
});ref.onDisposeref.invalidate(provider)ref.invalidateSelf()ref.refresh(provider)// In MyApp or a dedicated widget under ProviderScope:
Consumer(
builder: (context, ref, _) {
ref.watch(myEagerProvider); // forces initialization
return const MyApp();
},
)main()AsyncValue.requireValue
class TodosNotifier extends _$TodosNotifier {
Future<void> addTodo(Todo todo) async {
state = const AsyncLoading();
state = await AsyncValue.guard(() async {
await ref.read(repositoryProvider).addTodo(todo);
return [...?state.value, todo];
});
}
}
// In UI:
ElevatedButton(
onPressed: () => ref.read(todosNotifierProvider.notifier).addTodo(todo),
child: const Text('Add'),
)ref.readref.watchref.invalidateSelf()class MyObserver extends ProviderObserver {
void didUpdateProvider(ProviderObserverContext context, Object? previousValue, Object? newValue) {
print('[${context.provider}] updated: $previousValue → $newValue');
}
void providerDidFail(ProviderObserverContext context, Object error, StackTrace stackTrace) {
// Report to error service
}
}
runApp(ProviderScope(observers: [MyObserver()], child: MyApp()));// Unit test
final container = ProviderContainer(
overrides: [repositoryProvider.overrideWith((_) => FakeRepository())],
);
addTearDown(container.dispose);
expect(await container.read(todosProvider.future), isNotEmpty);
// Widget test
await tester.pumpWidget(
ProviderScope(
overrides: [repositoryProvider.overrideWith((_) => FakeRepository())],
child: const MyApp(),
),
);ProviderContainerProviderScopecontainer.listencontainer.readautoDisposeoverridesimplementswith MockProviderScope.containerOf(tester.element(...))