auto_go_route 2.0.0 copy "auto_go_route: ^2.0.0" to clipboard
auto_go_route: ^2.0.0 copied to clipboard

Type-safe, code-generated routing for Flutter. Full go_router feature parity with typed path and query parameters, shells, guards and transitions.

auto_go_route #

pub package generator CI pub points License: MIT

Type-safe, code-generated routing for Flutter, built on go_router.

Annotate your screens. The generator writes the go_router route table, typed URL builders and typed navigation helpers — so a wrong parameter is a compile error, not a blank screen in production.

@AutoGoRoute(path: '/products/:id')
class ProductPage extends StatelessWidget {
  const ProductPage({super.key, required this.id, this.tab});

  final int id;        // path parameter, decoded as an int
  final String? tab;   // query parameter, ?tab=
}

context.goToProductPage(id: 42, tab: 'reviews');   // → /products/42?tab=reviews

auto_go_route example app: generated routes, typed queries, typed path parameters and guards


Contents #

Highlights #

  • Routes live on the widget. No hand-maintained route table to drift out of sync with your screens.
  • Typed parameters end to end. int, double, bool, DateTime, Uri, BigInt, enums and lists are encoded into the URL and decoded back for you.
  • Generated navigation helpers. goTo…, pushTo…<T>, replaceWith…, replaceInPlaceWith… and locationOf… for every route.
  • Every go_router feature. Stateful and stateless shells, branch preloading, redirect, onEnter, onExit, metadata, observers, restoration, custom pages and dynamic RoutingConfig.
  • Errors at build time. Missing imports, unreachable parameters, duplicate names and invalid shells fail the build with a message that says how to fix them.
  • Every platform. Android, iOS, web, macOS, Windows and Linux.
  • AI-ready. Ships an llms.txt brief and copy-paste prompts for coding assistants.

Screenshots #

Captured from the example app — every screen is reached through a generated helper, and the bar at the top shows the real URL.

Home screen listing generated routes
Generated routes
Stateful shell with bottom navigation
Product list driven by enum and list query parameters
Typed queries
?sort=priceAsc&tags=new&tags=sale
Product details showing decoded path, query and extra values
Path, query and extra
Decoded by the generated builder
Sign-in screen reached through a guard redirect
Guards
Metadata-driven redirect to /login?from=…
Profile tabs inside a nested shell
Nested shells
A shell inside a stateful branch
Bottom sheet with its own URL
Adaptive overlays
A bottom sheet with a real URL

Why auto_go_route #

go_router is Flutter's recommended router, and this package does not replace it. It removes the boilerplate around it and makes the URL layer type-safe.

Plain go_router auto_go_route
Route table Written by hand, kept in sync by hand Generated from the widgets
Path parameters state.pathParameters['id']! — a String, checked at runtime final int id; — typed, checked at compile time
Navigating context.go('/products/$id') context.goToProductPage(id: 42)
Renaming a path Search and replace Change one annotation
Forgetting a parameter A 404 or a blank screen A compile error
go_router features All of them All of them — see parity

Compatibility #

auto_go_route go_router Flutter Dart
2.0.x >=17.5.0 <19.0.0 >=3.38.1 >=3.10.0
1.1.x ^16.2.4 >=3.29.0 >=3.7.0

Important

2.0.0 is a breaking release. Follow MIGRATION.md to upgrade from 1.x. If a 1.x build hangs or fails with Missing implementation of visitDotShorthandPropertyAccess, that is issue #3, and upgrading fixes it.

Getting started #

1. Install #

dependencies:
  auto_go_route: ^2.0.0

dev_dependencies:
  auto_go_route_generator: ^2.0.0
  build_runner: ^2.15.1
flutter pub get

auto_go_route re-exports go_router. You do not need a separate go_router dependency or import for GoRouter, GoRouterState or context.go.

Warning

Keep auto_go_route and auto_go_route_generator on the same major version. A 1.x generator still builds against the 2.x runtime, but it ignores 2.x annotation fields such as redirect: and metadata:, so guards silently stop running. flutter pub deps | grep auto_go_route should show both at 2.x.

2. Annotate a screen #

The widget is the route definition.

// lib/pages/home_page.dart
import 'package:auto_go_route/auto_go_route.dart';
import 'package:flutter/material.dart';

@AutoGoRoute(path: '/home', description: 'The landing screen.')
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) =>
      const Scaffold(body: Center(child: Text('Home')));
}

3. Create the router library #

The generated file is a part of this library, so everything the generated code refers to must be imported here: every annotated widget, the types of their parameters, and any guard, navigator key or page builder an annotation names. Flutter and go_router types are already covered by package:auto_go_route/auto_go_route.dart.

// lib/app_router.dart
import 'package:auto_go_route/auto_go_route.dart';

import 'pages/home_page.dart';        // …and every other annotated widget

part 'app_router.routes.g.dart';      // note the `.routes.g.dart` suffix

@AutoGoRouteBase(initialLocation: '/home')
class AppRouter extends _$AppRouter {}

Forget an import and the build fails with a list of the missing names, instead of leaving an Undefined name error inside a generated file.

4. Generate #

dart run build_runner build

Use dart run build_runner watch to regenerate on every save.

5. Wire it up #

Build the router once, outside any build method:

// lib/main.dart
final appRouter = AppRouter();
final router = appRouter.buildRouter();

void main() => runApp(MaterialApp.router(routerConfig: router));

buildRouter() accepts every GoRouter option as a named argument, each defaulting to the annotation. Pass runtime objects — which cannot appear in an annotation — here:

final router = appRouter.buildRouter(
  refreshListenable: authService,
  debugLogDiagnostics: kDebugMode,
  observers: [analyticsObserver],
);

6. Navigate #

context.goToHomePage();
context.pushToProductPage(id: 42, tab: 'reviews');
context.replaceWithProductPage(id: 43);

// The URL without navigating — for links, share sheets and tests.
final url = context.locationOfProductPage(id: 42);   // '/products/42'

Parameters #

Each constructor parameter is classified in this order:

  1. key, and a shell's child slot (its Widget or StatefulNavigationShell parameter), are skipped.
  2. An explicit @PathParam, @QueryParam, @RouteExtra or @RouteIgnore wins. It can go on the field or on the parameter.
  3. A name that appears in the route's resolved path is a path parameter.
  4. A URL-representable type is a query parameter.
  5. Anything else is read from state.extra.

A required parameter with no possible source is a build error, never a silently empty value.

Supported types #

String, int, double, num, bool, BigInt, DateTime, Uri and any enum, nullable or not. List<T> of those is supported for query parameters as a repeated key.

enum Sort { priceAsc, priceDesc, rating }

@AutoGoRoute(path: '/products')
class ProductList extends StatelessWidget {
  const ProductList({
    super.key,
    this.sort = Sort.rating,   // ?sort=priceAsc — the default applies when absent
    this.tags,                 // ?tags=new&tags=sale
    this.page = 1,             // ?page=2
    this.since,                // ?since=2026-01-01T00:00:00.000Z
  });

  final Sort sort;
  final List<String>? tags;
  final int page;
  final DateTime? since;
  // ...
}

context.goToProductList(sort: Sort.priceAsc, tags: const ['new'], page: 2);
// → /products?sort=priceAsc&tags=new&page=2

Renaming a parameter on the wire #

@AutoGoRoute(path: '/home')
class HomePage extends StatelessWidget {
  const HomePage({super.key, this.featureDisabled});

  // `feature-disabled` is not a valid Dart identifier.
  @QueryParam('feature-disabled')
  final bool? featureDisabled;
}

Objects in extra #

A parameter whose type cannot be written into a URL is read from state.extra. A route has one extra, so two such parameters is a build error.

@AutoGoRoute(path: '/products/:id')
class ProductPage extends StatelessWidget {
  const ProductPage({super.key, required this.id, this.product});

  final int id;
  final Product? product;   // from state.extra
}

context.pushToProductPage(id: 42, extra: product);

Tip

Make extra parameters nullable. extra is lost on deep links, browser reloads and state restoration, so a non-nullable one will throw the first time someone opens a shared URL.

Regular-expression constraints #

@AutoGoRoute(path: r'/users/:id(\d+)')   // digits only
class UserPage extends StatelessWidget {
  const UserPage({super.key, required this.id});
  final int id;
}

Optional path parameters #

go_router does not support them, so :id? is a build error. Use a nullable query parameter or declare two routes — see MIGRATION.md.

Handling malformed URLs

A value the URL cannot hold — /products/abc for an int id — throws RouteParamFormatException, naming the parameter, the expected type and the raw value. It is thrown while the page builds, so it does not reach GoRouter.onException, which only sees routing failures.

To send a malformed link somewhere useful, check it in a route redirect, where RouteCodec reads parameters the same way:

import 'dart:async';

FutureOr<String?> validId(BuildContext context, GoRouterState state) {
  try {
    return RouteCodec.optionalInt(state, 'id') == null ? '/not-found' : null;
  } on FormatException {
    return '/not-found';
  }
}

@AutoGoRoute(path: '/products/:id', redirect: 'validId')
class ProductPage extends StatelessWidget { /* ... */ }

Nesting #

Give a route a parent and keep its path relative. Path parameters from the parent are inherited.

@AutoGoRoute(path: '/products/:id')
class ProductPage extends StatelessWidget { /* ... */ }

@AutoGoRoute(path: 'reviews', parent: ProductPage)
class ReviewsPage extends StatelessWidget {
  const ReviewsPage({super.key, required this.id});
  final int id;   // inherited from /products/:id
}

// → /products/:id/reviews
context.goToReviewsPage(id: 42);

Shells #

Note

A go_router shell contributes no URL segment. A shell's children keep their own paths: a child declared as /general is reachable at /general, not /settings-area/general.

Bottom navigation (stateful shell) #

Each child becomes a StatefulShellBranch with its own navigator, so each tab keeps its state. order sets the branch — and tab — index.

@AutoGoRouteShell(path: '/', isStateful: true)
class DashboardShell extends StatelessWidget {
  const DashboardShell({super.key, required this.navigationShell});

  final StatefulNavigationShell navigationShell;

  @override
  Widget build(BuildContext context) => Scaffold(
    body: navigationShell,
    bottomNavigationBar: NavigationBar(
      selectedIndex: navigationShell.currentIndex,
      onDestinationSelected: (index) {
        final branch = DashboardShellBranch.values[index];
        // Tapping the active tab again returns it to its first screen.
        branch.go(navigationShell, initialLocation: branch.isActiveIn(navigationShell));
      },
      destinations: const [/* ... */],
    ),
  );
}

@AutoGoRoute(path: '/feed', parent: DashboardShell, order: 0)
@AutoGoRouteBranch(preload: true)   // build this branch eagerly
class FeedPage extends StatelessWidget { /* ... */ }

@AutoGoRoute(path: '/inbox', parent: DashboardShell, order: 1)
class InboxPage extends StatelessWidget { /* ... */ }

Every stateful shell gets a generated branch enum, built from the same order values as the branches, so tab indexes can never drift:

DashboardShellBranch.inboxPage.goFrom(context);   // from any screen inside the shell
DashboardShellBranch.of(navigationShell);         // the active branch
DashboardShellBranch.feedPage.initialLocation;    // '/feed'

Things to know:

  • A shell at / gets a generated redirect from / to its first branch, unless you declare a real route at /.
  • A stateful shell has no navigator of its own, so navigatorKey and observers go on @AutoGoRouteBranch. Setting them on the shell is a build error.
  • A branch whose first route has path parameters needs @AutoGoRouteBranch(initialLocation: ...), since go_router cannot derive one.

Shared chrome (stateless shell) #

A plain shell takes a Widget child:

@AutoGoRouteShell(path: '/settings-area', initialRoute: '/general')
class SettingsShell extends StatelessWidget {
  const SettingsShell({super.key, required this.child});
  final Widget child;
  // ...
}

@AutoGoRoute(path: '/general', parent: SettingsShell)
class GeneralSettingsPage extends StatelessWidget { /* ... */ }

A shell's own path redirects to initialRoute, or, when that is omitted, to its first child without path parameters. No redirect is generated over a real route at the same path.

Bottom sheets and dialogs with real URLs #

AdaptiveOverlayPage presents a shell's children as a bottom sheet on phones and a centred dialog on wide screens, while keeping the URL shareable:

Page<dynamic> sheetPageBuilder(
  BuildContext context,
  GoRouterState state,
  Widget child,
) => AdaptiveOverlayPage(child: child, heightFactor: 0.9);

@AutoGoRouteShell(path: '/compose', pageBuilder: 'sheetPageBuilder')
class ComposeShell extends StatelessWidget {
  const ComposeShell({super.key, required this.child});
  final Widget child;

  @override
  Widget build(BuildContext context) => child;
}

Guards #

Annotations refer to functions by name, as strings. The functions must be top-level or static and visible from the router library. Guards return FutureOr, which comes from dart:asyncauto_go_route does not re-export it, so add import 'dart:async'; to the library that declares them.

Route-level guards #

import 'dart:async';

FutureOr<String?> requireAuth(BuildContext context, GoRouterState state) =>
    authService.isLoggedIn ? null : '/login';

@AutoGoRoute(path: '/profile', middleware: ['requireAuth'])
class ProfilePage extends StatelessWidget { /* ... */ }

A route's redirect: runs first, then each middleware entry in order. The first non-null result wins.

Attach data to routes and write a single guard, so protecting a new route is a one-line change:

@AutoGoRoute(path: '/admin', metadata: {'requiresRole': 'admin'})
class AdminPage extends StatelessWidget { /* ... */ }

FutureOr<String?> appRedirect(BuildContext context, GoRouterState state) {
  final role = state.metadataAs<String>('requiresRole');
  if (role != null && authService.role != role) return '/home';
  return null;
}

@AutoGoRouteBase(redirect: 'appRedirect')
class AppRouter extends _$AppRouter {}

// Re-run redirects whenever the auth state changes.
final router = AppRouter().buildRouter(refreshListenable: authService);

Confirming before leaving #

Future<bool> confirmDiscard(BuildContext context, GoRouterState state) async =>
    await showDialog<bool>(context: context, builder: /* ... */) ?? false;

@AutoGoRoute(path: '/compose', onExit: 'confirmDiscard')
class ComposePage extends StatelessWidget { /* ... */ }

Intercepting every navigation #

@AutoGoRouteBase(onEnter: 'appOnEnter') maps to GoRouter.onEnter, which runs before any navigation and returns Allow() or Block.stop().

Transitions and pages #

@AutoGoRoute(
  path: '/sheet',
  transition: AutoRouteTransition.slideUp,
  transitionDurationMs: 250,
  opaque: false,
  barrierDismissible: true,
)
class SheetPage extends StatelessWidget { /* ... */ }

AutoRouteTransition offers platform (the default), material, cupertino, fade, slide, slideUp, slideDown, scale, rotation and none. For anything else, set pageBuilder: to the name of a Page<dynamic> Function(BuildContext, GoRouterState). buildAutoRoutePage() is public, so a custom page builder can reuse the built-in transitions.

Full-screen routes over a shell #

A route on the root navigator covers the bottom navigation bar. Declare the key on @AutoGoRouteBase too, so it is also the router's navigator key, as go_router requires:

final rootNavigatorKey = GlobalKey<NavigatorState>();

@AutoGoRoute(path: '/onboarding', parentNavigatorKey: 'rootNavigatorKey')
class OnboardingPage extends StatelessWidget { /* ... */ }

@AutoGoRouteBase(navigatorKey: 'rootNavigatorKey')
class AppRouter extends _$AppRouter {}

Dynamic routing #

GoRouter.routingConfig lets the available routes change at runtime — routes that only exist after sign-in, or a feature flag that removes a section — without rebuilding the router or losing navigation state:

final routingConfig = ValueNotifier(appRouter.buildRoutingConfig());
final router = appRouter.buildDynamicRouter(routingConfig: routingConfig);

// Later — the router picks the change up immediately.
routingConfig.value = appRouter.buildRoutingConfig(
  routes: [...appRouter.routes, ...featureRoutes],
  redirect: flagRedirect,
);

buildRoutingConfig defaults routes, redirect, onEnter and redirectLimit to the annotation; buildDynamicRouter defaults every other option, exactly like buildRouter.

Generated API #

For each route the generator emits a definition class and five navigation helpers:

class ProductPageRoute extends RoutePaths {
  static const String routeName = 'productPage';
  static const String routeTemplate = '/products/:id';

  String pathWith({required int id, String? tab, /* ... */});
}

extension AutoGoRouteNavigation on BuildContext {
  String locationOfProductPage({required int id, /* ... */});
  void goToProductPage({required int id, /* ... */});
  Future<T?> pushToProductPage<T extends Object?>({required int id, /* ... */});
  void replaceWithProductPage({required int id, /* ... */});         // pushReplacement
  void replaceInPlaceWithProductPage({required int id, /* ... */});  // replace
}

Every helper also accepts queries and fragment; all but locationOf… also accept extra, which a URL cannot carry. The route name defaults to the widget class name in lowerCamelCase; override it with @AutoGoRoute(name: ...).

On the router base:

appRouter.routes;                 // List<RouteBase> — compose it yourself
appRouter.allRoutes;              // every RoutePaths definition
appRouter.allShells;              // every ShellRoutePaths definition
appRouter.productPageRoute;       // one cached definition per route
appRouter.buildRouter(/* ... */);
appRouter.buildRoutingConfig();   // RoutingConfig, for dynamic routing
appRouter.buildDynamicRouter(routingConfig: /* ... */);

AppRoute.values;                  // an enum of every route, with routeName + template
DashboardShellBranch.values;      // one enum per stateful shell, in tab order

Rename the extension with @AutoGoRouteBase(navigatorExtensionName: 'AppNav'), or turn the enums off with generateRouteEnum: false.

go_router parity #

Every go_router 17.5+ feature is available through an annotation, a buildRouter() argument, or the generated routes list.

Show the full mapping
go_router How to reach it
GoRoute.builder The annotated widget
GoRoute.pageBuilder pageBuilder: or transition:
GoRoute.redirect redirect:, middleware:
GoRoute.onExit onExit:
GoRoute.parentNavigatorKey parentNavigatorKey:
GoRoute.caseSensitive caseSensitive: (also a router-wide default)
GoRoute.metadata metadata:, read with state.metadataAs<T>()
GoRoute.routes parent:
ShellRoute @AutoGoRouteShell
ShellRoute.navigatorKey / observers / restorationScopeId Same names on @AutoGoRouteShell (for a stateful shell, navigatorKey and observers go on @AutoGoRouteBranch)
ShellRouteBase.notifyRootObserver notifyRootObserver:
StatefulShellRoute.indexedStack @AutoGoRouteShell(isStateful: true)
StatefulShellRoute with a custom layout navigatorContainerBuilder:
StatefulShellBranch (navigatorKey, initialLocation, observers, restorationScopeId, preload) @AutoGoRouteBranch on the child
StatefulNavigationShell.goBranch The generated <Shell>Branch enum's go / goFrom
GoRouter.onEnter onEnter:
GoRouter.onException onException:
GoRouter.errorBuilder / errorPageBuilder errorBuilder: / errorPageBuilder: / errorWidget:
GoRouter.redirect / redirectLimit redirect: / redirectLimit:
GoRouter.refreshListenable refreshListenable:, or buildRouter()
GoRouter.observers / navigatorKey / restorationScopeId Same names, or buildRouter()
GoRouter.extraCodec extraCodec:
GoRouter.initialLocation / initialExtra Same names
GoRouter.routerNeglect / requestFocus / overridePlatformDefaultLocation / debugLogDiagnostics Same names, or buildRouter()
GoRouter.routingConfig / RoutingConfig buildDynamicRouter() / buildRoutingConfig()
CustomTransitionPage / NoTransitionPage transition:, or buildAutoRoutePage()
Anything else go_router is re-exported and appRouter.routes is public — build the GoRouter yourself

Introspection #

RouteRegistry indexes route definitions for validation, documentation and debug screens:

final registry = RouteRegistry.scoped()..registerAll(appRouter.allRoutes);

registry.validateAllRoutes();
registry.findClosest('/prodcuts');     // → the /products route
print(registry.generateDocumentation(format: DocumentationFormat.markdown));

Build configuration #

More than one router in a package

Each @AutoGoRouteBase scans the whole package by default, so two routers would both contain every route and declare the same public classes. Scope each one — the build warns until you do:

@AutoGoRouteBase(sourceGlobs: ['lib/admin/**/*.dart'])
class AdminRouter extends _$AdminRouter {}

A router library under test/ also scans test/. The builder only runs on lib/ and test/, so a router in integration_test/ or bin/ is not generated.

Narrowing the scan on large packages
# build.yaml
targets:
  $default:
    builders:
      auto_go_route_generator:auto_go_route_builder:
        options:
          source_globs:
            - lib/features/**/*.dart
            - lib/app_router.dart

Using with AI assistants #

auto_go_route ships llms.txt: a compact, authoritative brief written for AI coding assistants. It covers setup, the rules an assistant must follow, parameter classification, the generated API, shells, guards and common build errors. It is kept in step with each release, so it takes precedence over older examples an assistant may have learned from.

Give your assistant the brief #

Pick whichever fits your tools:

  • Paste a URL. Assistants that can browse can read it directly: https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt

  • Read it from the installed package. llms.txt is included in the published package. Agents with file access can find the package's rootUri in .dart_tool/package_config.json and read llms.txt there — it always matches the version you installed.

  • Vendor it into your repository and reference it from your assistant's project instructions (AGENTS.md, CLAUDE.md, .github/copilot-instructions.md, .cursor/rules/, and so on):

    curl -o docs/auto_go_route.llms.txt https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt
    
    ## Routing
    This app routes with auto_go_route. Before changing navigation, read
    docs/auto_go_route.llms.txt and follow its rules. Never edit *.routes.g.dart
    files; change the annotations and run `dart run build_runner build`.
    

Copy-paste prompts #

Each prompt is self-contained. Replace the text in <angle brackets>.

1. Add auto_go_route to a Flutter app
Set up type-safe routing in this Flutter app with auto_go_route 2.x.

First read https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt
and follow its rules exactly.

1. Add auto_go_route ^2.0.0 to dependencies, and auto_go_route_generator ^2.0.0
   and build_runner ^2.15.1 to dev_dependencies. Do not add go_router separately.
2. Annotate each screen widget with @AutoGoRoute. Turn the data each screen needs
   into typed constructor parameters: IDs in the path, filters and options as
   query parameters, and at most one nullable object per route in extra.
3. Create lib/app_router.dart with `part 'app_router.routes.g.dart';`, an
   @AutoGoRouteBase class, and imports for every annotated widget and every type
   their parameters use.
4. Build the router once in main.dart with AppRouter().buildRouter() and pass it
   to MaterialApp.router.
5. Run `dart run build_runner build`, then `flutter analyze`, and fix every
   issue by changing annotations or imports — never the generated file.
6. Replace string-based navigation with the generated context.goTo…/pushTo…
   helpers.

Screens: <list your screens, their paths and the data each one needs>
2. Convert an existing go_router configuration
Migrate this app's hand-written go_router configuration to auto_go_route 2.x,
keeping every URL and behaviour the same. If a route cannot be expressed with
auto_go_route annotations (for example one widget built by two GoRoutes, or a
StatefulShellBranch with several root routes), stop and list it instead of
changing its URL.

Read https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt
first and follow its rules.

- Find the GoRouter setup (<path to the router file>). For every GoRoute, add
  @AutoGoRoute to the widget it builds, keeping the same path and name. Nested
  GoRoutes become `parent:` with a relative path.
- Replace manual reads of state.pathParameters, state.uri.queryParameters and
  state.extra with typed constructor parameters. Keep the existing wire names
  with @PathParam / @QueryParam where they differ from the Dart names.
- ShellRoute becomes @AutoGoRouteShell; StatefulShellRoute becomes
  @AutoGoRouteShell(isStateful: true), with branch options on
  @AutoGoRouteBranch and the child's `order:` matching its current tab index.
- Route redirects become `redirect:` or `middleware:` naming top-level
  functions; router-level options move to @AutoGoRouteBase, and runtime objects
  such as refreshListenable are passed to buildRouter().
- Replace context.go('/path/$id') and context.push(...) calls with the generated
  helpers.
- Run `dart run build_runner build`, `flutter analyze` and `flutter test`.
  Finish with a table mapping every old path to its new route class.
3. Upgrade from auto_go_route 1.x to 2.0
Upgrade this project from auto_go_route 1.x to 2.0.

Read the migration guide first:
https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/MIGRATION.md
and the assistant brief:
https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt

1. Bump auto_go_route and auto_go_route_generator to ^2.0.0 and build_runner to
   ^2.15.1, as described in section 1. Remove any direct go_router dependency
   unless other code needs it.
2. Run `dart run build_runner build` and fix every build error by following the
   fix included in its message.
3. Work through each other MIGRATION.md section that applies to this project —
   for example typed navigation call sites, buildRouter, optional path
   parameters, parameter sources and error handlers.
4. Run `flutter analyze` and `flutter test`, and replace deprecated APIs with
   the replacements named in their deprecation messages.
5. Summarise each change and link the MIGRATION.md section that required it.
4. Add a new screen
Add a new screen using auto_go_route. Follow the rules in llms.txt
(https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt).

- Screen: <ScreenName>
- Path: <for example /orders/:orderId>
- Parent route or shell: <none, or the parent widget class>
- Data it needs: <for example orderId (int, path), status (OrderStatus enum,
  optional query), order (Order object, optional extra)>

Create the widget with @AutoGoRoute and typed constructor parameters, import it
in the router library, run `dart run build_runner build`, and navigate to it
from <existing screen> with the generated goTo… helper. Do not edit generated
files or build URLs by hand.
5. Protect routes with a guard
Add authentication guards with auto_go_route, following llms.txt
(https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt).

- Mark protected routes with `metadata: {'requiresAuth': true}` on their
  @AutoGoRoute annotations: <list the routes>.
- Write one top-level `FutureOr<String?> appRedirect(BuildContext context,
  GoRouterState state)` in the router library (import dart:async for FutureOr). It reads
  state.metadataAs<bool>('requiresAuth') and redirects signed-out users to
  /login?from=<the original location, URI-encoded>.
- Register it with @AutoGoRouteBase(redirect: 'appRedirect').
- Pass the auth state as buildRouter(refreshListenable: ...) so redirects
  re-run on sign-in and sign-out.
- After sign-in, return the user to the `from` location.
- Add widget tests for the signed-out redirect and the return after sign-in.
6. Add bottom navigation
Add bottom navigation with preserved tab state using auto_go_route, following
llms.txt (https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt).

- Create a shell widget annotated @AutoGoRouteShell(path: '/', isStateful: true)
  that takes a StatefulNavigationShell and renders a NavigationBar.
- Tabs, in order: <for example Home /home, Search /search, Account /account>.
  Give each tab's root screen `parent: <ShellName>` and `order: <index>`.
- Switch tabs with the generated <ShellName>Branch enum — never raw indexes —
  and reset a tab when its active destination is tapped again.
- Screens that must cover the navigation bar use
  parentNavigatorKey: 'rootNavigatorKey', with
  @AutoGoRouteBase(navigatorKey: 'rootNavigatorKey').
- Run build_runner and add a widget test that switches tabs and checks that each
  tab keeps its state.
7. Fix a build or runtime error
My auto_go_route build or navigation is failing. Using the rules and the
troubleshooting section in
https://rawgit.flutter-io.cn/itsarvinddev/auto_go_route/main/llms.txt,
find the root cause and fix it in the annotations, imports or router library —
never in *.routes.g.dart. Then run `dart run build_runner build` and
`flutter analyze` to confirm the fix.

auto_go_route version: <from pubspec.lock>
Flutter version: <flutter --version>
Error output:
<paste the full error>

Reviewing AI-generated routing code #

Before accepting a change, check that it:

  • ❌ declares routes with annotations and does not edit *.routes.g.dart;
  • ❌ names guards, keys and builders as strings that are imported into the router library;
  • ❌ navigates with generated helpers instead of string paths;
  • ❌ keeps extra parameters nullable, with at most one per route;
  • ❌ does not use :param? optional path segments;
  • ❌ builds the router once, outside build();
  • ❌ passes dart run build_runner build, flutter analyze and flutter test.

Troubleshooting #

The build hangs, or fails with Missing implementation of visitDotShorthandPropertyAccess

A 1.x generator pinned analyzer: ^7, which cannot parse Dart 3.10+ syntax. Upgrade to auto_go_route_generator: ^2.0.0 (issue #3).

A member named 'pushNamed' is defined in ... and neither is more specific

A 1.x extension collided with go_router's own. Upgrade to 2.0, which removes that method.

Undefined name inside the generated file

Something an annotation names by string is not visible from the router library. 2.0 detects this at build time and lists the missing names. If you still see it, the identifier is declared in a part that loads after the generated file.

build_runner does not write the generated file

The part directive must be exactly part '<your file>.routes.g.dart';.

A route is missing from the generated table

The generator scans this package's lib/ by default. Routes in a different package are not discovered; move them, or widen sourceGlobs / source_globs.

Still stuck? Open an issue with your auto_go_route version, flutter --version and the full error.

Example app #

example/ is a complete app that exercises every feature in this README: a stateful shell with preloaded branches, a nested shell with its own navigator, typed enum and list query parameters, a digits-only path constraint, metadata-driven guards, an onExit confirmation, a full-screen route over the shell, and an adaptive bottom-sheet flow. Its widget tests drive the generated router end to end.

cd example
flutter run

Contributing #

Issues and pull requests are welcome — see CONTRIBUTING.md for the development workflow and the checks CI runs.

License #

MIT — see LICENSE.

Made with ❤️ by Arvind Sangwan · X

10
likes
160
points
72
downloads
screenshot

Documentation

API reference

Publisher

verified publishermohesu.com

Weekly Downloads

Type-safe, code-generated routing for Flutter. Full go_router feature parity with typed path and query parameters, shells, guards and transitions.

Repository (GitHub)
View/report issues
Contributing

Topics

#routing #navigation #go-router #codegen #type-safety

Funding

Consider supporting this project:

github.com

License

MIT (license)

Dependencies

equatable, flutter, go_router

More

Packages that depend on auto_go_route