--- description: "Riverpod conventions for {{PROJECT_NAME}}" alwaysApply: true --- # Riverpod Standards — {{PROJECT_NAME}} ## Provider types | Type | Use case | |------|----------| | `AsyncNotifier` | Async state from {{BACKEND}} | | `Notifier` | Synchronous derived/local UI state | | `StreamNotifier` | Real-time subscriptions | | `@riverpod` function | Simple computed/derived values | ## Code generation (mandatory) ```dart // ✅ Always use @riverpod annotation @riverpod class AuthNotifier extends _$AuthNotifier { @override Future build() => ref.watch(authRepositoryProvider).currentUser(); Future login(String email, String password) async { state = const AsyncLoading(); state = await AsyncValue.guard( () => ref.read(authRepositoryProvider).login(email, password), ); } } // ❌ Never write manual provider declarations final authProvider = StateNotifierProvider>( (ref) => AuthNotifier(), ); // DON'T DO THIS ``` ## Rules - `ref.watch()` inside `build()` ONLY — **never** `ref.read()` inside `build()` - `ref.read(provider.notifier).method()` for mutations in gesture handlers - `ref.invalidate(provider)` to refresh — never manually reset state to `AsyncLoading()` - Family providers for parameterized data: `productDetailsProvider(productId)` - Providers scoped at feature level; core providers in `lib/core/di/` ## AsyncValue in widgets Every `AsyncValue` MUST handle all three states: ```dart ref.watch(productsProvider).when( data: (products) => ProductList(products: products), loading: () => const ProductListShimmer(), // required error: (e, _) => ErrorWidget(error: e), // required ) ``` ## File locations in {{PROJECT_NAME}} - `lib/features/[feature]/[feature]_provider.dart` (generated: `[feature]_provider.g.dart`) - `lib/features/[feature]/[feature]_repository.dart` - Run `dart run build_runner watch` during development