riverpod
Skill669 starsChanged 3 months agoMITView on GitHub
Use when setting up providers, combining requests, managing state disposal, passing arguments, performing side effects, or testing providers (Riverpod).
In this repository36 other files it gives its agents.
- AGENTS.mdAGENTS.md
- accessibilitySkill
- architecture-feature-firstSkill
- blocSkill
- code-reviewSkill
- dart-3-updatesSkill
- developing-genkit-dartSkill
- effective-dartSkill
- firebase-aiSkill
- firebase-analyticsSkill
- firebase-app-checkSkill
- firebase-authSkill
- firebase-cloud-firestoreSkill
- firebase-cloud-functionsSkill
- firebase-crashlyticsSkill
- firebase-databaseSkill
- firebase-data-connectSkill
- firebase-in-app-messagingSkill
- firebase-messagingSkill
- firebase-remote-configSkill
- firebase-storageSkill
- flutter-app-architectureSkill
- flutter-best-practicesSkill
- flutter-change-notifierSkill
- flutter-errorsSkill
- flutterfire-configureSkill
- flutter-pre-cachingSkill
- flutter-use-column-row-firstSkill
- generate-images-with-firebase-aiSkill
- inclusive-designSkill
- mockitoSkill
- mocktailSkill
- patrol-e2e-testingSkill
- providerSkill
- revenuecat-testingSkill
- testingSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
25 rules. About 0 are common, 4 are distinctive, and 21 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
21
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 52
Use Provider, FutureProvider, or StreamProvider based on the return type.
- Very specific: line 53
Use ConsumerWidget or ConsumerStatefulWidget instead of StatelessWidget/StatefulWidget when accessing providers.
- Very specific: line 84
Never: call ref.watch inside callbacks, listeners, or Notifier methods.
- Very specific: line 85
Use ref.read(yourNotifierProvider.notifier).method() to call Notifier methods from the UI.
- Very specific: line 86
Check context.mounted before using ref after an await in async callbacks.
- Very specific: line 100
Use ref.watch(asyncProvider.future) to await an async provider's resolved value.
- Very specific: line 117
Always enable autoDispose for parameterized providers to prevent memory leaks.
- Very specific: line 118
Use Dart 3 records or code generation for multiple parameters — they naturally override ==.
- Very specific: line 119
Avoid passing plain List or Map as parameters (no == override).
- Very specific: line 120
Use the provider_parameters lint rule from riverpod_lint to catch equality mistakes.
- Very specific: line 138
Use ref.onDispose for cleanup.
- Very specific: line 139
Use ref.invalidate(provider) to force destruction.
- Very specific: line 140
Use ref.refresh(provider) to invalidate and immediately read the new value — always use the return value.
- Very specific: line 158
Place eager initialization in a public widget (not main()) for consistent test behavior.
- Very specific: line 159
Use AsyncValue.requireValue to read data directly and throw clearly if not ready.
- Very specific: line 184
Use ref.read (not ref.watch) in event handlers.
- Very specific: line 185
After a side effect, update state by: setting it directly, calling ref.invalidateSelf(), or manually updating the cache.
- Distinctive: line 186
Always handle loading and error states in the UI.
- Distinctive: line 187
Do not perform side effects in provider constructors or build methods.
- Very specific: line 231
Create a new ProviderContainer or ProviderScope for each test — never share state between tests.
- Very specific: line 232
Use container.listen over container.read for autoDispose providers to keep state alive during the test.
- Very specific: line 233
Use overrides to inject mocks or fakes.
- Distinctive: line 234
Prefer mocking dependencies (repositories) rather than Notifiers directly.
- Very specific: line 235
If you must mock a Notifier, subclass the original — don't use implements or with Mock.
- Distinctive: line 236
Place Notifier mocks in the same file as the Notifier if using code generation.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Riverpod Skill
This skill defines how to correctly use Riverpod for state management in Flutter and Dart applications.
1. Setup
void main() {
runApp(const ProviderScope(child: MyApp()));
}- Wrap your app with
ProviderScopedirectly inrunApp— never insideMyApp. - Install and use
riverpod_lintto enable IDE refactoring and enforce best practices.
2. Defining Providers
// 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 { ... }
}- Define all providers as
finaltop-level variables. - Use
Provider,FutureProvider, orStreamProviderbased on the return type. - Use
ConsumerWidgetorConsumerStatefulWidgetinstead ofStatelessWidget/StatefulWidgetwhen accessing providers.
3. Using Ref
| 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;
});- Never call
ref.watchinside callbacks, listeners, or Notifier methods. - Use
ref.read(yourNotifierProvider.notifier).method()to call Notifier methods from the UI. - Check
context.mountedbefore usingrefafter anawaitin async callbacks.
4. Combining Providers
@riverpod
Future<String> userGreeting(Ref ref) async {
final user = await ref.watch(userProvider.future);
return 'Hello, ${user.name}!';
}- Use
ref.watch(asyncProvider.future)to await an async provider's resolved value. - Providers only execute once and cache the result — multiple widgets listening to the same provider share one computation.
5. Passing Arguments (Families)
@riverpod
Future<Todo> todo(Ref ref, String id) async {
return ref.watch(repositoryProvider).fetchTodo(id);
}
// Usage
final todo = ref.watch(todoProvider('some-id'));- Always enable
autoDisposefor parameterized providers to prevent memory leaks. - Use
Dart 3 recordsor code generation for multiple parameters — they naturally override==. - Avoid passing plain
ListorMapas parameters (no==override); useconstcollections, records, or classes with proper equality. - Use the
provider_parameterslint rule fromriverpod_lintto catch equality mistakes.
6. Auto Dispose & State Lifecycle
- 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
.autoDisposeto enable disposal. - State is always destroyed when a provider is recomputed.
// keepAlive with timer
ref.onCancel(() {
final link = ref.keepAlive();
Timer(const Duration(minutes: 5), link.close);
});- Use
ref.onDisposefor cleanup; do not trigger side effects or modify providers inside it. - Use
ref.invalidate(provider)to force destruction; useref.invalidateSelf()from within the provider. - Use
ref.refresh(provider)to invalidate and immediately read the new value — always use the return value.
7. Eager Initialization
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();
},
)- Place eager initialization in a public widget (not
main()) for consistent test behavior. - Use
AsyncValue.requireValueto read data directly and throw clearly if not ready.
8. Performing Side Effects
@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'),
)- Use
ref.read(notref.watch) in event handlers. - After a side effect, update state by: setting it directly, calling
ref.invalidateSelf(), or manually updating the cache. - Always handle loading and error states in the UI.
- Do not perform side effects in provider constructors or build methods.
9. Provider Observers
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()));10. Testing
// 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
ProviderContainerorProviderScopefor each test — never share state between tests. - Use
container.listenovercontainer.readforautoDisposeproviders to keep state alive during the test. - Use
overridesto inject mocks or fakes. - Prefer mocking dependencies (repositories) rather than Notifiers directly.
- If you must mock a Notifier, subclass the original — don't use
implementsorwith 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(...)).
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

