All playbooksFlutter Production App

Add a screen with a route and state

Add a feature screen reachable by name and by deep link, with Riverpod state, translated texts and a test that runs without network.

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 in application, contracts in domain, implementations in data. Widgets never call Supabase directly.
  • Routes are declared once in the AppRoute enum and opened by name. The router redirect already 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

  1. Add the route to AppRoute in lib/app/router/app_routes.dart, for example notes(path: '/notes'). Keep paths internal (/...): internalLocation drops anything else that arrives from a deep link.
  2. Register a GoRoute in lib/app/router/app_router.dart with path: AppRoute.notes.path and name: 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 the from parameter.
  3. Create the state in lib/features/notes/application/notes_providers.dart. Read data with a FutureProvider, or an AsyncNotifierProvider if the screen also changes it, that waits for authSessionProvider as currentProfileProvider does. Take the repository from its own provider so tests can override it.
  4. Create lib/features/notes/presentation/notes_page.dart as a ConsumerWidget, like SettingsPage: an AppBar with AppBackButton, content limited to 560 px, and ref.watch(notesProvider).when(...) for loading, error and data, plus an empty state. Use a ConsumerStatefulWidget only for local UI state such as a text controller.
  5. Add the texts to app_en.arb (the template file), app_de.arb and app_it.arb. fvm flutter pub get regenerates context.l10n.
  6. Add the entry point, for example a button in HomePage that calls context.pushNamed(AppRoute.notes.name).
  7. Add a widget test that overrides the repository with a fake in a ProviderScope, as test/critical_user_journey_test.dart does. 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.