brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
riverpod-patternsThis 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.
Independent GitHub snapshot · Automated classification · Original documentation governs
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.