When to use it
A new screen reached from the app, with its own state: a list, a detail page, a form.
Rules to keep
- Feature-first: the screen lives in
lib/features/<feature>/presentation, providers inapplication, contracts indomain, implementations indata. Widgets never call Supabase directly. - Routes are declared once in the
AppRouteenum and opened by name. The routerredirectalready handles session, onboarding and deep links: do not add a second guard inside the page. - A router guard is a convenience, not authorization: protected data is enforced by RLS.
- State lives in Riverpod providers, not in singletons or global variables. Loading, empty and error states are explicit.
- Every visible text goes through
lib/l10n/*.arb, in all three files.
Steps
- Add the route to
AppRouteinlib/app/router/app_routes.dart, for examplenotes(path: '/notes'). Keep paths internal (/...):internalLocationdrops anything else that arrives from a deep link. - Register a
GoRouteinlib/app/router/app_router.dartwithpath: AppRoute.notes.pathandname: AppRoute.notes.name. Do not add it to_gates, which holds only session, auth and onboarding: outside the gates the redirect protects the route and, after sign-in, returns to it through thefromparameter. - Create the state in
lib/features/notes/application/notes_providers.dart. Read data with aFutureProvider, or anAsyncNotifierProviderif the screen also changes it, that waits forauthSessionProviderascurrentProfileProviderdoes. Take the repository from its own provider so tests can override it. - Create
lib/features/notes/presentation/notes_page.dartas aConsumerWidget, likeSettingsPage: anAppBarwithAppBackButton, content limited to 560 px, andref.watch(notesProvider).when(...)for loading, error and data, plus an empty state. Use aConsumerStatefulWidgetonly for local UI state such as a text controller. - Add the texts to
app_en.arb(the template file),app_de.arbandapp_it.arb.fvm flutter pub getregeneratescontext.l10n. - Add the entry point, for example a button in
HomePagethat callscontext.pushNamed(AppRoute.notes.name). - Add a widget test that overrides the repository with a fake in a
ProviderScope, astest/critical_user_journey_test.dartdoes. It opens the screen and checks loading, empty, data and error states, without network.
Done when
- the verification commands pass;
- the screen opens from the app and from a deep link after sign-in;
- the test covers loading, empty, data and error states without network;
- the new widgets contain no hard-coded text.
