一键导入
riverpod
Use when setting up providers, combining requests, managing state disposal, passing arguments, performing side effects, or testing providers (Riverpod).
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when setting up providers, combining requests, managing state disposal, passing arguments, performing side effects, or testing providers (Riverpod).
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when working on accessibility, a11y, WCAG, ARIA, screen readers, keyboard nav, focus order, contrast, alt text, captions, reduced motion, or target sizes; not language/culture/device (see inclusive-design).
Use when creating a feature, designing folder structure, adding repositories/services/view models, wiring dependency injection, or deciding which layer owns logic.
Use when creating a Cubit or Bloc, modeling state with sealed classes or status enums, wiring BlocBuilder/BlocListener/BlocProvider, writing bloc tests, or choosing between Cubit and Bloc.
Use when asked to review a PR, MR, branch, or diff, audit changed files, or check code quality.
Use when writing switch statements, refactoring if-else chains, creating data classes, choosing records vs classes, destructuring values, or modernizing pre-Dart-3 code.
Use when writing Dart code, reviewing for style, refactoring naming, adding doc comments, structuring imports, or enforcing type annotations.
| name | riverpod |
| description | Use when setting up providers, combining requests, managing state disposal, passing arguments, performing side effects, or testing providers (Riverpod). |
This skill defines how to correctly use Riverpod for state management in Flutter and Dart applications.
void main() {
runApp(const ProviderScope(child: MyApp()));
}
ProviderScope directly in runApp — never inside MyApp.riverpod_lint to enable IDE refactoring and enforce best practices.// Functional provider (codegen)
@riverpod
int example(Ref ref) => 0;
// FutureProvider (codegen)
@riverpod
Future<List<Todo>> todos(Ref ref) async {
return ref.watch(repositoryProvider).fetchTodos();
}
// Notifier (codegen)
@riverpod
class TodosNotifier extends _$TodosNotifier {
@override
Future<List<Todo>> build() async {
return ref.watch(repositoryProvider).fetchTodos();
}
Future<void> addTodo(Todo todo) async { ... }
}
final top-level variables.Provider, FutureProvider, or StreamProvider based on the return type.ConsumerWidget or ConsumerStatefulWidget instead of StatelessWidget/StatefulWidget when accessing providers.| Method | Use for |
|---|---|
ref.watch | Reactively listen — rebuilds when value changes. Use during build phase only. |
ref.read | One-time access — use in callbacks/Notifier methods, not in build. |
ref.listen | Imperative subscription — prefer ref.watch where possible. |
ref.onDispose | Cleanup when provider state is destroyed. |
// In a widget
class MyWidget extends ConsumerWidget {
@override
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.watch inside callbacks, listeners, or Notifier methods.ref.read(yourNotifierProvider.notifier).method() to call Notifier methods from the UI.context.mounted before using ref after an await in async callbacks.@riverpod
Future<String> userGreeting(Ref ref) async {
final user = await ref.watch(userProvider.future);
return 'Hello, ${user.name}!';
}
ref.watch(asyncProvider.future) to await an async provider's resolved value.@riverpod
Future<Todo> todo(Ref ref, String id) async {
return ref.watch(repositoryProvider).fetchTodo(id);
}
// Usage
final todo = ref.watch(todoProvider('some-id'));
autoDispose for parameterized providers to prevent memory leaks.Dart 3 records or code generation for multiple parameters — they naturally override ==.List or Map as parameters (no == override); use const collections, records, or classes with proper equality.provider_parameters lint rule from riverpod_lint to catch equality mistakes.keepAlive: true..autoDispose to enable disposal.// keepAlive with timer
ref.onCancel(() {
final link = ref.keepAlive();
Timer(const Duration(minutes: 5), link.close);
});
ref.onDispose for cleanup; do not trigger side effects or modify providers inside it.ref.invalidate(provider) to force destruction; use ref.invalidateSelf() from within the provider.ref.refresh(provider) to invalidate and immediately read the new value — always use the return value.Providers are lazy by default. To eagerly initialize:
// In MyApp or a dedicated widget under ProviderScope:
Consumer(
builder: (context, ref, _) {
ref.watch(myEagerProvider); // forces initialization
return const MyApp();
},
)
main()) for consistent test behavior.AsyncValue.requireValue to read data directly and throw clearly if not ready.@riverpod
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.read (not ref.watch) in event handlers.ref.invalidateSelf(), or manually updating the cache.class MyObserver extends ProviderObserver {
@override
void didUpdateProvider(ProviderObserverContext context, Object? previousValue, Object? newValue) {
print('[${context.provider}] updated: $previousValue → $newValue');
}
@override
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(),
),
);
ProviderContainer or ProviderScope for each test — never share state between tests.container.listen over container.read for autoDispose providers to keep state alive during the test.overrides to inject mocks or fakes.implements or with Mock.ProviderScope.containerOf(tester.element(...)).