State
The Riverpod patterns Kite uses — and the ones it avoids.
Riverpod 3, no code generation
Codegen is deliberately off. A template is read far more than it is extended,
and part 'x.g.dart' plus a build_runner step is a
wall between a reader and what the code does. Every provider here is written
by hand and visible.
The providers
| Provider | Type | Holds |
|---|---|---|
dataProvider | Provider | The DataProvider implementation — the one line you swap |
sessionProvider | NotifierProvider | The signed-in user, or null |
themeProvider | NotifierProvider | Theme mode and accent |
localeProvider | NotifierProvider | Active locale |
listParamsProvider | NotifierProvider | Query state per resource |
listProvider | FutureProvider.family | A page of rows, keyed by resource |
recordProvider | FutureProvider.family | One record, keyed by (resource, id) |
What Kite avoids
StateProvider is legacy in Riverpod 3 and is not used
here. Query state is a single Notifier holding a map keyed by
resource — fewer moving parts to explain than a provider family, and a
template should not teach a deprecated API.
class ListParamsController extends Notifier<Map<String, ListParams>> {
@override
Map<String, ListParams> build() => const {};
ListParams of(String resource) => state[resource] ?? initial;
void update(String resource, ListParams p) => state = {...state, resource: p};
void reset(String resource) => update(resource, initial);
}
Refetching
listProvider watches listParamsProvider, so changing
the search, a filter or the page refetches automatically. After a mutation,
invalidate explicitly:
await ref.read(dataProvider).delete(resource, id);
ref.invalidate(listProvider(resource));
ref.invalidate(recordProvider((resource, id)));
The session is in-memory on purpose
Signing in sets a SessionUser; reloading the page signs you out.
Persisting it would mean picking a storage package and a token format on your
behalf — decisions that belong to your auth provider, not to a template.
Swap SessionController.signIn for a real call and the guard,
redirects and every screen keep working unchanged. If you want persistence,
that method and build() are the two places to touch.
Local widget state stays local
Form fields, toggles, the selected kanban column, which inbox message is open
— all StatefulWidget. Riverpod is for state that outlives a
screen or is shared across them. Putting a text controller in a provider buys
nothing and costs a rebuild.