add-route
Skill0 starsChanged 56 days agoMITView on GitHub
Register a screen with AutoRoute — the @RoutePage annotation, router entry, path and naming conventions, route parameters, nested routes, guards, modals and dialogs, and the AppNavigator methods states call. Use when adding a screen to the router, adding a guard, or navigating somewhere new.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- swapnil-saal/agentic-react-startertheir own version, first seen Sep 14, 2026
- Bufferoverflovv/FastMCP-Blueprinttheir own version, first seen Apr 5, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- Bufferoverflovv/FastMCP-Blueprint★ 0same name
- swapnil-saal/agentic-react-starter★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository18 other files it gives its agents.
- payload/AGENTS.mdAGENTS.md
- add-design-tokenSkill
- add-localizationSkill
- add-platform-channelSkill
- create-adrSkill
- create-api-providerSkill
- create-ds-componentSkill
- create-dtoSkill
- create-featureSkill
- create-pageSkill
- create-storeSkill
- create-use-caseSkill
- handle-errorsSkill
- plan-featureSkill
- qa-featureSkill
- triage-bug-reportSkill
- write-maestro-flowSkill
- write-testsSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Run melos run build — this generates the {PageName}Route class in app_router.gr.dart.
- Very specific: line 14
Add an AutoRoute entry to AppRouter.routes.
- Very specific: line 137
Every path must end in exactly one of resolver.next() or resolver.redirectUntil(...).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Add a route
Governed by ADR-0008. The router lives in lib/core/navigation/; guards in lib/core/guards/.
The loop
- Annotate the page with
@RoutePage()(create-page). - Run
melos run build— this generates the{PageName}Routeclass inapp_router.gr.dart. - Add an
AutoRouteentry toAppRouter.routes. - Navigate to it from a state class via
AppNavigator. Never from a widget.
Router entry
AppRouter is not registered in DI. AppNavigator (a @singleton) builds it and hands it its guards, which get their dependencies through the constructor (ADR-0007):
@AutoRouterConfig(replaceInRouteName: 'Page,Route')
class AppRouter extends RootStackRouter {
AppRouter({required AuthGuard authGuard}) : _authGuard = authGuard;
final AuthGuard _authGuard;
@override
late final List<AutoRoute> routes = [
AutoRoute(
initial: true,
path: '/',
page: SplashRoute.page,
// No guard: SplashPageState decides between home and login.
),
AutoRoute(path: '/login', page: LoginRoute.page),
AutoRoute(path: '/home', page: HomeRoute.page, guards: [_authGuard]),
// Illustrative: nested routes. The template has none.
AutoRoute(
path: '/dashboard',
page: DashboardRoute.page,
guards: [_authGuard],
meta: const {'description': 'Signed-in dashboard with its tabs'},
children: [
AutoRoute(path: 'agenda', page: AgendaRoute.page),
AutoRoute(path: 'meetings', page: MeetingsListRoute.page),
],
),
];
}The real router is lib/core/navigation/app_router.dart; it also has /todos/:id and /challenges/joined, both guarded.
replaceInRouteName: 'Page,Route'is whyLoginPagegeneratesLoginRoute. Always use the generated class, never a string.- Paths are kebab-case and describe the page:
/login,/sign-up-with-email. Children use relative paths. - Give a new route
meta: {'description': …}; it is what makes the router readable a year later. The template's own routes do not carry one yet. - The initial route is not guarded. A guard on
/let a signed-in user through to the splash page and left them there.SplashPageState.initreads the token and replaces the stack with Home or Login.
Parameters
Passed through the generated route's constructor, const wherever possible:
await _appNavigator.push(TodoDetailsRoute(todoId: id));
await _appNavigator.push(const SettingsRoute());Optional values are named parameters. Do not pass objects a deep link could not reconstruct — pass an id and let the target load it.
Navigating — from the state class only
// ❌ FORBIDDEN, anywhere in a widget
context.router.push(const SettingsRoute());
// ✅ inject AppNavigator into the state
abstract class _MyPageStateBase with Store {
final AppNavigator _appNavigator;
_MyPageStateBase(this._appNavigator);
@action
Future<void> onSettingsPressed() =>
_appNavigator.push(const SettingsRoute());
}Pages call the state method. If a navigation decision has real logic in it, extract a Navigation use case (create-use-case).
AppNavigator methods
| Method | Does |
push(route) | push a new route |
pop<T>({result}) | pop the current route |
popUntilRoot() | pop to the root |
popUntilRouteWithName(name) | pop back to a named route |
replace(route) | replace the current route |
pushAndPopAll(route) | replace the whole stack — use after login and logout |
navigatePath(path) | navigate by path string (deep-link style) |
canPop() | whether the router can pop |
showModal({builder, …}) | modal bottom sheet |
showScaffoldMessage(widget) | bottom overlay notification, used by IssueTrackingService |
showFullScreenLoading() / hideFullScreenLoading() | blocking spinner dialog, and pop it |
openPhoneSettings() | the app's page in system settings |
showAppDialog, showErrorMessage and showSuccessMessage also exist, but their bodies are empty: calling them shows nothing. Implement one before a state relies on it.
Guards
Guards extend AutoRouteGuard, live in lib/core/guards/, and are listed on the route they protect. They are not registered in DI: AppNavigator constructs each one with its dependencies and passes it to AppRouter (AppRouter(authGuard: AuthGuard(authStore))), so a new guard is a new AppRouter constructor parameter too.
class AuthGuard extends AutoRouteGuard {
AuthGuard(this._authStore);
final AuthStore _authStore;
@override
Future<void> onNavigation(
NavigationResolver resolver,
StackRouter router,
) async {
// Reads the stored token, which sets isAuthed on a cold start.
await _authStore.getAccessToken();
if (!_authStore.isAuthed) {
resolver.redirectUntil(const LoginRoute());
return;
}
resolver.next();
}
}Every path must end in exactly one of resolver.next() or resolver.redirectUntil(...). A guard that returns without calling either hangs the navigation with no error.
Modals and dialogs
Reached through AppNavigator, not showModalBottomSheet directly, so the state layer stays the only thing that knows about presentation:
await _appNavigator.showModal(builder: (_) => const AddTodoModal());A modal with its own state is not a feature (ADR-0005): it has no route. It lives inside the feature that opens it, at lib/features/{feature}/modals/{modal}/, with view/ for the widget, mobx/ for its state and use_cases/ when it has one, as home/modals/add_todo_modal/ does (create-feature). Its widget is a page root: Provider(create: (_) => getIt<AddTodoModalState>()).
E2E
clearState: true in a Maestro flow clears the auth token, so splash sends the app to login and a guarded route redirects there. Flows start from the shared common/start_home.yaml subflow rather than assuming a signed-in state (write-maestro-flow).
Checklist
@RoutePage()on the page,melos run buildrunAutoRouteentry added with a kebab-case path and ametadescription; no guard on the initial route- Generated
{Page}Routeclass used, never a path string - Parameters are ids, not objects a deep link could not rebuild
- Navigation only from a state class via
AppNavigator; nocontext.routeranywhere - Guards live in
lib/core/guards/, are constructed byAppNavigatorwith their dependencies, and every path callsnext()orredirectUntil() - Deep link into the route works from a cold start
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

