riverpod
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRiverpod Skill
Riverpod 使用技能
This skill defines how to correctly use Riverpod for state management in Flutter and Dart applications.
本技能定义了如何在Flutter和Dart应用中正确使用Riverpod进行状态管理。
1. Setup
1. 配置
dart
void main() {
runApp(const ProviderScope(child: MyApp()));
}- Wrap your app with directly in
ProviderScope— never insiderunApp.MyApp - Install and use to enable IDE refactoring and enforce best practices.
riverpod_lint
dart
void main() {
runApp(const ProviderScope(child: MyApp()));
}- 在中直接用
runApp包裹你的应用——绝对不要在ProviderScope内部包裹。MyApp - 安装并使用以启用IDE重构功能并遵循最佳实践。
riverpod_lint
2. Defining Providers
2. 定义Providers
dart
// 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 { ... }
}- Define all providers as top-level variables.
final - Use ,
Provider, orFutureProviderbased on the return type.StreamProvider - Use or
ConsumerWidgetinstead ofConsumerStatefulWidget/StatelessWidgetwhen accessing providers.StatefulWidget
dart
// Functional provider (代码生成)
int example(Ref ref) => 0;
// FutureProvider (代码生成)
Future<List<Todo>> todos(Ref ref) async {
return ref.watch(repositoryProvider).fetchTodos();
}
// Notifier (代码生成)
class TodosNotifier extends _$TodosNotifier {
Future<List<Todo>> build() async {
return ref.watch(repositoryProvider).fetchTodos();
}
Future<void> addTodo(Todo todo) async { ... }
}- 将所有Providers定义为顶级final变量。
- 根据返回类型选择使用、
Provider或FutureProvider。StreamProvider - 当需要访问Providers时,使用或
ConsumerWidget替代ConsumerStatefulWidget/StatelessWidget。StatefulWidget
3. Using Ref
3. 使用Ref
| 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. |
dart
// 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;
});- Never call inside callbacks, listeners, or Notifier methods.
ref.watch - Use to call Notifier methods from the UI.
ref.read(yourNotifierProvider.notifier).method() - Check before using
context.mountedafter anrefin async callbacks.await
| 方法 | 用途 |
|---|---|
| 响应式监听——当值变化时触发重建。仅在构建阶段使用。 |
| 一次性访问——在回调/Notifier方法中使用,不要在构建阶段使用。 |
| 命令式订阅——尽可能优先使用 |
| 当Provider状态被销毁时执行清理操作。 |
dart
// 在Widget中
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final value = ref.watch(myProvider);
return Text('$value');
}
}
// 在Provider中进行清理
final provider = StreamProvider<int>((ref) {
final controller = StreamController<int>();
ref.onDispose(controller.close);
return controller.stream;
});- 绝对不要在回调、监听器或Notifier方法中调用。
ref.watch - 使用从UI中调用Notifier的方法。
ref.read(yourNotifierProvider.notifier).method() - 在异步回调中的之后使用
await前,检查ref。context.mounted
4. Combining Providers
4. 组合Providers
dart
Future<String> userGreeting(Ref ref) async {
final user = await ref.watch(userProvider.future);
return 'Hello, ${user.name}!';
}- Use to await an async provider's resolved value.
ref.watch(asyncProvider.future) - Providers only execute once and cache the result — multiple widgets listening to the same provider share one computation.
dart
Future<String> userGreeting(Ref ref) async {
final user = await ref.watch(userProvider.future);
return 'Hello, ${user.name}!';
}- 使用来等待异步Provider的解析值。
ref.watch(asyncProvider.future) - Providers仅执行一次并缓存结果——多个监听同一个Provider的Widget会共享同一个计算结果。
5. Passing Arguments (Families)
5. 传递参数(Families)
dart
Future<Todo> todo(Ref ref, String id) async {
return ref.watch(repositoryProvider).fetchTodo(id);
}
// Usage
final todo = ref.watch(todoProvider('some-id'));- Always enable for parameterized providers to prevent memory leaks.
autoDispose - Use or code generation for multiple parameters — they naturally override
Dart 3 records.== - Avoid passing plain or
Listas parameters (noMapoverride); use==collections, records, or classes with proper equality.const - Use the lint rule from
provider_parametersto catch equality mistakes.riverpod_lint
dart
Future<Todo> todo(Ref ref, String id) async {
return ref.watch(repositoryProvider).fetchTodo(id);
}
// 使用方式
final todo = ref.watch(todoProvider('some-id'));- 始终为参数化Providers启用以防止内存泄漏。
autoDispose - 对于多个参数,使用或代码生成——它们会自动重写
Dart 3 records方法。== - 避免传递普通的或
List作为参数(没有重写Map);使用==集合、records或具有正确相等性的类。const - 使用中的
riverpod_lintlint规则来捕获相等性错误。provider_parameters
6. Auto Dispose & State Lifecycle
6. 自动销毁与状态生命周期
- With codegen: state is destroyed by default when no longer listened to. Opt out with .
keepAlive: true - Without codegen: state is kept alive by default. Use to enable disposal.
.autoDispose - State is always destroyed when a provider is recomputed.
dart
// keepAlive with timer
ref.onCancel(() {
final link = ref.keepAlive();
Timer(const Duration(minutes: 5), link.close);
});- Use for cleanup; do not trigger side effects or modify providers inside it.
ref.onDispose - Use to force destruction; use
ref.invalidate(provider)from within the provider.ref.invalidateSelf() - Use to invalidate and immediately read the new value — always use the return value.
ref.refresh(provider)
- 使用代码生成时:默认情况下,当不再被监听时状态会被销毁。可以通过选择退出该机制。
keepAlive: true - 不使用代码生成时:默认情况下状态会被保留。使用启用销毁机制。
.autoDispose - 当Provider被重新计算时,状态总会被销毁。
dart
// 通过计时器保持存活
ref.onCancel(() {
final link = ref.keepAlive();
Timer(const Duration(minutes: 5), link.close);
});- 使用进行清理操作;不要在其中触发副作用或修改Providers。
ref.onDispose - 使用强制销毁状态;在Provider内部使用
ref.invalidate(provider)。ref.invalidateSelf() - 使用来销毁并立即读取新值——务必使用返回值。
ref.refresh(provider)
7. Eager Initialization
7. 预初始化
Providers are lazy by default. To eagerly initialize:
dart
// In MyApp or a dedicated widget under ProviderScope:
Consumer(
builder: (context, ref, _) {
ref.watch(myEagerProvider); // forces initialization
return const MyApp();
},
)- Place eager initialization in a public widget (not ) for consistent test behavior.
main() - Use to read data directly and throw clearly if not ready.
AsyncValue.requireValue
Providers默认是懒加载的。要实现预初始化:
dart
// 在MyApp中或ProviderScope下的专用Widget中:
Consumer(
builder: (context, ref, _) {
ref.watch(myEagerProvider); // 强制初始化
return const MyApp();
},
)- 将预初始化放在公共Widget中(而非)以确保一致的测试行为。
main() - 使用直接读取数据,如果数据未准备好则会抛出清晰的错误。
AsyncValue.requireValue
8. Performing Side Effects
8. 执行副作用
dart
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'),
)- Use (not
ref.read) in event handlers.ref.watch - After a side effect, update state by: setting it directly, calling , or manually updating the cache.
ref.invalidateSelf() - Always handle loading and error states in the UI.
- Do not perform side effects in provider constructors or build methods.
dart
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];
});
}
}
// 在UI中:
ElevatedButton(
onPressed: () => ref.read(todosNotifierProvider.notifier).addTodo(todo),
child: const Text('Add'),
)- 在事件处理程序中使用(而非
ref.read)。ref.watch - 副作用执行完成后,通过以下方式更新状态:直接设置状态、调用或手动更新缓存。
ref.invalidateSelf() - 始终在UI中处理加载和错误状态。
- 不要在Provider构造函数或构建方法中执行副作用。
9. Provider Observers
9. Provider 观察者
dart
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()));dart
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) {
// 向错误服务上报
}
}
runApp(ProviderScope(observers: [MyObserver()], child: MyApp()));10. Testing
10. 测试
dart
// 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(),
),
);- Create a new or
ProviderContainerfor each test — never share state between tests.ProviderScope - Use over
container.listenforcontainer.readproviders to keep state alive during the test.autoDispose - Use to inject mocks or fakes.
overrides - Prefer mocking dependencies (repositories) rather than Notifiers directly.
- If you must mock a Notifier, subclass the original — don't use or
implements.with Mock - Place Notifier mocks in the same file as the Notifier if using code generation.
- Obtain the container in widget tests with .
ProviderScope.containerOf(tester.element(...))
dart
// 单元测试
final container = ProviderContainer(
overrides: [repositoryProvider.overrideWith((_) => FakeRepository())],
);
addTearDown(container.dispose);
expect(await container.read(todosProvider.future), isNotEmpty);
// Widget测试
await tester.pumpWidget(
ProviderScope(
overrides: [repositoryProvider.overrideWith((_) => FakeRepository())],
child: const MyApp(),
),
);- 为每个测试创建一个新的或
ProviderContainer——绝对不要在测试之间共享状态。ProviderScope - 对于Providers,使用
autoDispose而非container.listen以在测试期间保持状态存活。container.read - 使用注入mocks或fakes。
overrides - 优先mock依赖项(如仓库)而非直接mock Notifiers。
- 如果必须mock Notifier,继承原始类——不要使用或
implements。with Mock - 如果使用代码生成,将Notifier mocks放在与Notifier相同的文件中。
- 在Widget测试中,通过获取container。
ProviderScope.containerOf(tester.element(...))