home / skills / yelmuratoff / agent_sync / routing
This skill helps you implement robust Flutter navigation with GoRouter, enabling nested routes, deep links, and type-safe arguments across screens.
npx playbooks add skill yelmuratoff/agent_sync --skill routingReview the files below or copy the command above to add this skill to your agents.
---
name: routing
description: When adding screens, deep links, or complex navigation flows using `go_router`.
---
# Routing (GoRouter)
## When to use
- Adding a new screen or feature entry point.
- Implementing deep linking or redirection (e.g., AuthGuard).
- Passing arguments between screens.
## Setup
Define a centralized `local_router.dart` (or similar) in `core/router/` or `app/router/`.
Prefer nested/sub-routes over a flat list so deep links and back navigation remain predictable.
```dart
final goRouter = GoRouter(
initialLocation: '/',
debugLogDiagnostics: true,
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
routes: [
GoRoute(
path: 'details/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailPage(id: id);
},
),
],
),
],
);
```
## Best Practices
### 1) Type-Safe Arguments
**Preferred: `go_router_builder` with `@TypedGoRoute`**
When `go_router_builder` is available, annotate route data classes:
```dart
@TypedGoRoute<OrdersRoute>(name: 'orders', path: '/orders')
@immutable
class OrdersRoute extends GoRouteData {
const OrdersRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const OrdersScreen();
}
}
// With parameters:
@TypedGoRoute<OrderDetailRoute>(name: 'order-detail', path: '/orders/:id')
@immutable
class OrderDetailRoute extends GoRouteData {
const OrderDetailRoute({required this.id});
final String id;
@override
Widget build(BuildContext context, GoRouterState state) {
return OrderDetailScreen(id: id);
}
}
// Navigate:
const OrdersRoute().go(context);
OrderDetailRoute(id: orderId).go(context);
```
**Fallback (no go_router_builder)**: Use `goNamed` with string constants defined in a central route-names file. Never inline path strings in widget code.
- Use path parameters for IDs (e.g. `details/:id`).
- Use query parameters for filtering/sorting state (for example: `?status=paid&page=2`).
- Use `extra` for complex objects **only if necessary**. Prefer passing an ID and refetching data to ensure the screen is independent and deep-linkable.
- Prefer typed routes or `goNamed` over raw path strings where possible.
- Keep route path segments lowercase `kebab-case` (for example `user/update-address`).
### 2) Redirects (Guards)
- implement `redirect` logic at the top level or per-route.
```dart
redirect: (context, state) {
final isLoggedIn = authBloc.state.isAuthenticated;
final isLoggingIn = state.uri.path == '/login';
if (!isLoggedIn && !isLoggingIn) return '/login';
if (isLoggedIn && isLoggingIn) return '/';
return null;
},
```
### 3) Navigation
- Use `context.go('/details/123')` or `context.goNamed(...)` for normal app/deep-linkable navigation.
- Use `context.push('/details/123')` when the route is transient and should return a result on pop.
- Prefer `BuildContext` extensions (`context.goNamed`, `context.pushNamed`) over `GoRouter.of(context)` for consistency.
This skill helps integrate and manage navigation using go_router in Flutter apps. It focuses on adding screens, deep links, redirects, and type-safe argument passing so navigation stays predictable and testable. The guidance covers setup, routing patterns, and common pitfalls to avoid.
Define a centralized router object and organize routes with nested sub-routes to preserve back navigation and deep-link behavior. Prefer typed route classes (via go_router_builder/@TypedGoRoute) or named routes for safer navigation and parameter handling. Implement redirect logic at the top level or per-route to enforce auth and other guards.
Should I pass complex objects via extra?
Avoid it when possible. Prefer passing an ID and reloading data so screens are deep-linkable and independent; use extra only for transient state that cannot be refetched.
Where should redirect logic live?
Place auth or global redirect logic at the top-level router or on specific route entries to keep it predictable and centralized.
When to use push vs go?
Use go/goNamed for normal navigation and deep-linkable routes. Use push/pushNamed for transient routes that should return a result on pop.