# `riverpod`

| Package | Kind | Provides the role | Needs | Choose with |
| --- | --- | --- | --- | --- |
| [`smf_riverpod`](https://pub.dev/packages/smf_riverpod) | infrastructure | State management | nothing | `-m riverpod` |

`riverpod` manages the state of screens with [Riverpod](https://riverpod.dev) 3, through [flutter_riverpod](https://pub.dev/packages/flutter_riverpod), without code generation.

## What it adds to the app

The module adds `flutter_riverpod: ^3.4.3` to the dependencies, and a `ProviderScope` around the root widget in `lib/main.dart`:

```dart
runApp(ProviderScope(child: const App()));
```

In the [guide for coding agents](https://doc.saymyframe.com/guides/coding-agents.md) of the app, the module writes the section "State management". The state of a screen is a `NotifierProvider.autoDispose` in the directory of its feature, written by hand. The screen is a `ConsumerWidget` that passes the state on to a view that imports no `flutter_riverpod`. State that several screens read is a provider without `autoDispose`. After an `await`, a `Notifier` checks `ref.mounted` before it sets `state`, and it reads what the call needs from `ref` before the `await`. A widget talks only to providers, never to a service or a DI container, and the `ProviderScope` stays above every widget that reads one.

## Features with state

A feature that keeps state supports Riverpod through a variant keyed by the id of this module, `riverpod`. The variant brings the providers of the feature's screens. A provider that needs a service of the DI container gets it in the feature's composition file, through a bridge such as `Provider<CounterStore>((_) => resolve<CounterStore>())`. The variant depends on `flutter_riverpod` with the constraint `any`, so the version comes from this module. See [Write a feature module](https://doc.saymyframe.com/extending/feature-module.md).

Among the built-in features, [`sign_in`](https://doc.saymyframe.com/modules/sign-in.md) has such a variant:

- Each of its screens has a provider in a file next to it, with its `Notifier`, such as `signInProvider` in `lib/features/sign_in/sign_in_notifier.dart`. It is a `NotifierProvider.autoDispose`, so it lasts as long as its screen. The screen is a `ConsumerWidget` that watches the provider and gives its view the state. The view is a plain Flutter widget that imports nothing of `flutter_riverpod`.
- State that several screens read is a provider without `autoDispose`. `sessionProvider` in `lib/features/sign_in/session_provider.dart` is one, and a widget reads who uses the app with `ref.watch(sessionProvider)`.
- The notifiers reach the session of the app through `appSessionProvider`, which a test overrides with a session of its own.
- A notifier reads what it needs from `ref` before an `await`, and checks `ref.mounted` after it, since the user may have left the screen while the call was on its way.
- A provider is one for the app, so two pages of one screen that are open at once show the same state.

`home`, `onboarding` and `settings` keep no state in a state manager, so they work the same with `riverpod` and without it.

## The `ProviderScope` and other wrappers

The `ProviderScope` has to be above every widget that reads a provider. Other modules can wrap the root widget too, and their wrappers come in the order of their modules: a module comes after the providers of the roles it requires and after the modules it depends on. A module can use Riverpod only through its variant for `riverpod`, which makes it require the state management role, or by depending on this module. Either way it comes after `riverpod`, so its wrapper always goes inside the `ProviderScope`.

## Choosing it

`smf create` asks which module manages the state of the app, and offers [`bloc`](https://doc.saymyframe.com/modules/bloc.md) and None as well. To choose it without the question:

```bash
smf create my_app -m riverpod
```

An app has at most one module that manages its state.
