协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
riverpod-patterns【开发与工程】This skill provides Riverpod state management patterns and best practices for Flutter applications. Use when reviewing or writing Riverpod providers, AsyncValue handling, ref usage, or provider lifecycle management.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Iron Law: Always use the correct Riverpod provider type for the use case; never use StateProvider for async data or FutureProvider for mutable state.
Correct Riverpod patterns for Flutter state management with code_generation style.
When to use: Writing or reviewing Riverpod providers, AsyncNotifier, AsyncValue.when, ref.watch vs ref.read, family providers, or provider lifecycle.
Process:
reference/riverpod-core-patterns.md for code examples and rules// ✅ Read-only async data (API call, DB read)
@riverpod
Future<List<User>> userList(UserListRef ref) async {
return ref.watch(userRepositoryProvider).getAll();
}
// ✅ Mutable state with async operations
@riverpod
class UserNotifier extends _$UserNotifier {
@override
Future<User> build(String userId) async {
return ref.watch(userRepositoryProvider).getById(userId);
}
Future<void> update(UserUpdateDto dto) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() =>
ref.read(userRepositoryProvider).update(dto));
}
}
// ❌ WRONG — never use StateProvider for async data
final userProvider = StateProvider<User?>((ref) => null); // no async support
// ✅ All 3 states handled visibly
ref.watch(userListProvider).when(
data: (users) => UserListWidget(users: users),
loading: () => const CircularProgressIndicator(),
error: (err, stack) => ErrorWidget(message: err.toString()), // never swallow
);
// ❌ WRONG — skipLoadingOnReload hides loading state from user
ref.watch(userListProvider).when(
skipLoadingOnReload: true, // only acceptable after explicit UX decision
data: (users) => UserListWidget(users: users),
loading: () => const SizedBox.shrink(), // invisible loading = silent failure
error: (err, stack) => const SizedBox.shrink(), // swallowed error
);
// ✅ ref.watch — only inside build()
@override
Widget build(BuildContext context, WidgetRef ref) {
final users = ref.watch(userListProvider); // rebuilds on change
return UserListWidget(users: users.valueOrNull ?? []);
}
// ✅ ref.read — only inside callbacks/event handlers
void onButtonPressed(WidgetRef ref) {
ref.read(userNotifierProvider.notifier).refresh(); // one-time action
}
// ❌ WRONG — ref.watch in a callback (causes infinite rebuild)
onPressed: () => ref.watch(userNotifierProvider.notifier).refresh(),
| File | Contents | Load When |
|---|---|---|
reference/riverpod-core-patterns.md | Code examples, provider types, ref usage rules | Writing providers, ref.watch/read, AsyncValue handling |
reference/riverpod-review-checklist.md | Review checklist for Riverpod code audits (used by riverpod-reviewer agent) | Reviewing Riverpod code, running riverpod-reviewer agent |
Provider not found: Ensure ProviderScope wraps the widget tree. Check that generated .g.dart files are up to date (dart run build_runner build).
AsyncValue stuck loading: Verify the repository method returns data. Use AsyncValue.guard() to catch and surface errors instead of silent failures.