---
name: flutter-riverpod-state
description: Implements feature-first Flutter state management with Riverpod providers, notifiers, AsyncValue, streams, families, and provider overrides. Use when choosing a provider, building a controller or repository, wiring a HookConsumerWidget, or testing Riverpod state.
---

# Flutter State with Riverpod

## Choose the provider

- Use `Provider<T>` for immutable dependencies, services, and derived synchronous values.
- Use `NotifierProvider<Notifier, T>` for mutable synchronous state with commands.
- Use `FutureProvider<T>` for read-only asynchronous data with no mutation methods.
- Use `AsyncNotifierProvider<Notifier, T>` for asynchronous state plus refresh, submit, or mutation methods.
- Use `StreamProvider<T>` for values that continue arriving, such as realtime rows, connectivity, or prices.
- Add `.family` when identity depends on one argument and `.autoDispose` when route-scoped state should be released.
- Prefer code generation only when multi-argument families would otherwise require record or value-object plumbing.

## Keep the data flow one-way

1. The view watches controller state and sends user intent to controller methods.
2. The controller owns loading, data, and error transitions.
3. The repository reads clients through `Ref`, performs I/O, parses responses, and returns models.
4. Provider invalidation or dependency changes drive fresh data back toward the view.

Do not call Dio, Supabase, Firebase, or persistence clients directly from a view or controller when a repository can own that boundary.

## Implement async state

1. Model server data as `AsyncValue<T>`.
2. Set `state = const AsyncLoading()` before an explicit mutation.
3. Await the repository call inside the controller.
4. Set `AsyncData(value)` on success and `AsyncError(error, stack)` on failure.
5. In the view, handle loading, error, and data exhaustively with `when`, `switch`, or equivalent pattern matching.
6. Give error states a user-safe message and an explicit retry action.

## Use Ref deliberately

- Use `ref.watch(provider)` during build or inside a provider when recomputation should follow changes.
- Use `ref.read(provider)` in callbacks and command methods for one-time access.
- Use `ref.listen(provider, ...)` for side effects such as snackbars, analytics, or navigation.
- Use `ref.invalidate(provider)` to discard cached state and trigger a clean recomputation.
- Watch the smallest useful projection with `select` when rebuild scope matters.

## View rules

- Prefer `HookConsumerWidget` for views.
- Keep business decisions out of widgets; hooks are only for UI-local state and controllers.
- Read `.notifier` in event handlers, not as reactive view data.
- Keep repository and controller providers near their feature.
- Use `ProviderScope` overrides for environment wiring and tests.

## Test checklist

- Override external client or repository providers in a fresh `ProviderContainer`.
- Verify initial, loading, success, and failure states in order.
- Test family arguments and disposal behavior where they affect correctness.
- Assert retries invalidate or rerun the intended provider.
- Dispose the container after every test.
