[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-9303":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":16,"starSnapshotCount":16,"syncStatus":34,"lastSyncTime":35,"discoverSource":36},9303,"auto_route_library","Milad-Akarie\u002Fauto_route_library","Milad-Akarie","Flutter route generator","",null,"Dart",1748,471,16,239,0,1,4,56.92,"MIT License",false,"master",true,[25,26,27,28,29,30],"autoroute","deeplinking","flutter","navigation","nested-routes","router","2026-06-12 04:00:43","\u003Cp align=\"center\">\n  \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fmaster\u002Fart\u002Fauto_route_logo.svg\" height=\"170\" alt=\"auto_route_logo\">\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-green\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-green\" alt=\"MIT License\">\n  \u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fstargazers\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002FMilad-Akarie\u002Fauto_route_library?style=flat&logo=github&colorB=green&label=stars\" alt=\"stars\">\n  \u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fpub.dev\u002Fpackages\u002Fauto_route\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fpub\u002Fv\u002Fauto_route.svg?label=pub&color=orange\" alt=\"pub version\">\n  \u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fx3SBU4WRRd\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fdiscord\u002F821043906703523850.svg?color=7289da&label=Discord&logo=discord&style=flat-square\" alt=\"Discord Badge\">\n  \u003C\u002Fa>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fwww.buymeacoffee.com\u002Fmiladakarie\" target=\"_blank\">\n    \u003Cimg src=\"https:\u002F\u002Fcdn.buymeacoffee.com\u002Fbuttons\u002Fv2\u002Fdefault-yellow.png\" alt=\"Buy Me A Coffee\" height=\"30px\" width= \"108px\">\n  \u003C\u002Fa>\n\u003C\u002Fp>\n\n---\n\n- [Introduction](#introduction)\n  - [Installation](#installation)\n  - [Setup and Usage](#setup-and-usage)\n  - [Usage without code generation](#usage-without-code-generation)\n- [Generated routes](#generated-routes)\n- [Navigation](#navigating-between-screens)\n  - [Navigating Between Screens](#navigating-between-screens)\n  - [Passing Arguments](#passing-arguments)\n  - [Returning Results](#returning-results)\n  - [Nested navigation](#nested-navigation)\n  - [Tab Navigation](#tab-navigation)\n    - [Using PageView](#using-pageview)\n    - [Using TabBar](#using-tabbar)\n  - [Finding The Right Router](#finding-the-right-router)\n  - [Navigating Without Context](#navigating-without-context)\n- [Deep Linking](#deep-linking)\n- [Declarative Navigation](#declarative-navigation)\n- [Working with Paths](#working-with-paths)\n- [Route guards](#route-guards)\n- [Wrapping routes](#wrapping-routes)\n- [Navigation Observers](#navigation-observers)\n- [Customization](#customizations)\n  - [Custom Route Transitions](#custom-route-transitions)\n  - [Custom Route Builder](#custom-route-builder)\n- [Others](#others)\n  - [Including Micro\u002FExternal Packages](#including-microexternal-packages)\n  - [Configuring builders](#configuring-builders)\n    - [Optimizing Generation Time](#optimizing-generation-time)\n    - [Enabling Cached Builds (Experimental)](#enabling-cached-builds)\n  - [AutoLeadingButton-BackButton](#autoleadingbutton-backbutton)\n  - [ActiveGuardObserver](#activeguardobserver)\n  - [Android Predictive Back](#android-predictive-back)\n- [Examples](#examples)\n\n## LeanBuilder support (Experimental) ⚡\nauto_route_generator: (10.2.3+) now supports [LeanBuilder](https:\u002F\u002Fpub.dev\u002Fpackages\u002Flean_builder) for super-fast incremental builds.\n\nto use lean_builder instead of build_runner, simply add it to your dev_dependencies:\n\n```yaml\ndev_dependencies:\n  auto_route_generator: \u003Clatest-version>\n  lean_builder: \u003Clatest-version>\n```\n\nand then run the builder using:\nfor one-time build\n```terminal\ndart run lean_builder build\n```\nfor watching files and rebuilding on changes\n```terminal\ndart run lean_builder watch\n```\n\nto disable `auto_route_generator` from using build_runner, you can add the following to your `build.yaml` file:\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_route_generator:\n        enabled: false\n      auto_route_generator:auto_router_generator:\n        enabled: false\n```\n\n## Migration guides\n\n- [Migrating to v9](https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fblob\u002Fmaster\u002Fmigrations\u002Fmigrating_to_v9.md)\n- [Migrating to v6](https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fblob\u002Fmaster\u002Fmigrations\u002Fmigrating_to_v6.md)\n\n## Old documentation\n\n- [Pre v9 documentation](https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fblob\u002Fmaster\u002Fold\u002Fpre_v9_README.md)\n- [Pre v6 documentation](https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fblob\u002Fmaster\u002Fold\u002Fpre_v6_README.md)\n\n## Introduction\n\n#### What is AutoRoute?\n\nIt’s a Flutter navigation package, it allows for strongly-typed arguments passing, effortless deep-linking and it uses code generation to simplify routes setup. With that being said, it requires a minimal amount of code to generate everything needed for navigation inside of your App.\n\n#### Why AutoRoute?\n\nIf your App requires deep-linking or guarded routes or just a clean routing setup, you'll need to use named\u002Fgenerated routes and you’ll end up writing a lot of boilerplate code for mediator argument classes, checking for required arguments, extracting arguments and a bunch of other stuff. **AutoRoute** does all that for you and much more.\n\n## Installation\n\nAdd the following dependencies to your `pubspec.yaml` file:\n\n\n ```yaml\ndependencies:\n  auto_route: [latest-version]\n\ndev_dependencies:\n  auto_route_generator: [latest-version]\n  build_runner:\n```\n\n```terminal\nflutter pub add auto_route dev:auto_route_generator dev:build_runner\n```\n\n## Setup And Usage\n\n1. Create a router class and annotate it with `@AutoRouterConfig` then extend \"RootStackRouter\" from The auto_route package\n2. Override the routes getter and start adding your routes.\n\n```dart\n@AutoRouterConfig()\nclass AppRouter extends RootStackRouter {\n\n  @override\n  List\u003CAutoRoute> get routes => [\n    \u002F\u002F\u002F routes go here\n  ];\n}\n```\n\n### Using part builder\n\nTo generate a part-of file simply add a `part` directive to your `AppRouter`.\n\n**Note:** The `deferredLoading` functionality does not work with part-file setup.\n\n### Generating Routable pages\n\nRoutable pages are just simple everyday widgets annotated with `@RoutePage()` which allows them to be constructed by the router.\n\n```dart\n@RoutePage()\nclass HomeScreen extends StatefulWidget {}\n```\n\n#### Now simply run the generator\n\nUse the [watch] flag to watch the files' system for edits and rebuild as necessary.\n\n```terminal\ndart run build_runner watch\n```\n\nIf you want the generator to run one time and exit, use\n\n```terminal\ndart run build_runner build\n```\n\n#### Add the generated route to your routes list\n\n```dart\n@AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route')\nclass AppRouter extends RootStackRouter {\n\n  @override\n  RouteType get defaultRouteType => RouteType.material(); \u002F\u002F.cupertino, .adaptive ..etc\n\n  @override\n  List\u003CAutoRoute> get routes => [\n    \u002F\u002F HomeScreen is generated as HomeRoute because\n    \u002F\u002F of the replaceInRouteName property\n    AutoRoute(page: HomeRoute.page),\n  ];\n\n  @override\n  List\u003CAutoRouteGuard> get guards => [\n    \u002F\u002F optionally add root guards here\n  ];\n}\n```\n\n#### Finalize the setup\n\nAfter you run the generator, your router class will be generated. Then simply hook it up with your MaterialApp.\n\n```dart\n\u002F\u002F assuming this is the root widget of your App\nclass App extends StatelessWidget {\n  \u002F\u002F make sure you don't initiate your router\n  \u002F\u002F inside of the build function.\n  final _appRouter = AppRouter();\n\n  @override\n  Widget build(BuildContext context){\n    return MaterialApp.router(\n      routerConfig: _appRouter.config(),\n    );\n  }\n}\n```\n## Usage without code generation\nYou can use auto_route without code generation by providing an inline `NamedRouteDef` with a page builder function, as name suggests, you must provide a name for the route so you can navigate to it later by name or path.\n### Declaring Named Routes\n```dart\n  NamedRouteDef(\n    name: 'BookDetailsRoute',\n    path: '\u002Fbooks\u002F:id', \u002F\u002F optional\n    builder: (context, data) {\n      return BookDetailsPage(id: data.params.getInt('id'));\n    },\n  ),\n```\n`NamedRouteDef` is a wrapper around `AutoRoute` that allows you to provide a page builder function instead of a generated page, it has the same properties as `AutoRoute` except for `page` properties.\n\n**Tip:** use the shorthand version of NamedRouteRef for a cleaner look\n```dart\n   routes: [\n     .named('HomeRoute', (ctx, data) => HomePage()),\n     .named('BookListRoute', (ctx, data) => BookListPage()),\n    ];\n```\n\n### Navigating to Named Routes\nYou can either navigate to a named route by using the dynamic `NamedRoute` class to match by name or by using the `pushPath` method to match by path.\n```dart\n \u002F\u002F\u002F match by name\n    router.push(NamedRoute('BookDetailsRoute', params: {'id': 1}));\n    router.replace(NamedRoute('BookDetailsRoute', params: {'id': 1}));\n    router.navigate(NamedRoute('BookDetailsRoute', params: {'id': 1}));\n \u002F\u002F\u002F or use the shorthand version\n    router.push(.named('BookDetailsRoute',) params: {'id': 1}));\n    \n\u002F\u002F\u002F match by path\n     router.pushPath('\u002Fbooks\u002F1');\n     router.replacePath('\u002Fbooks\u002F1');\n     router.navigatePath('\u002Fbooks\u002F1');\n```\n\n*Note:* You can mix and match between named and generated routes.\n\n### Building a router without code generation\n\nYou can either extend `RootStackRouter` like you would normally do or use the `RootStackRouter.build` constructor to build a router without code generation.\n```dart\n    final router = RootStackRouter.build(\n      defaultRouteType: ...,\n      guards: [global guards],\n      routes: [\n        NamedRouteDef(\n          name: 'BookDetailsRoute',\n          path: '\u002Fbooks\u002F:id', \u002F\u002F optional\n          builder: (context, data) {\n            return BookDetailsPage(id: data.params.getInt('id'));\n          },\n        ),\n         \u002F\u002F ... other routes\n      ],\n    );\n ```\n## Generated Routes\n\nA `PageRouteInfo` object will be generated for every declared **AutoRoute**. These objects hold strongly-typed page arguments which are extracted from the page's default constructor. Think of them as string path segments on steroids.\n\n```dart\nclass BookListRoute extends PageRouteInfo {\n  const BookListRoute({\n    List\u003CPagerouteInfo>? children,\n  }) : super(name, initialChildren: children);\n\n  static const String name = 'BookListRoute';\n  static const PageInfo page = PageInfo(name,builder: (...));\n}\n```\n\n## Navigating Between Screens\n\n`AutoRouter` offers the same known push, pop and friends methods to manipulate the pages stack using both the generated `PageRouteInfo` objects and paths.\n\n```dart\n\u002F\u002F get the scoped router by calling\nAutoRouter.of(context);\n\u002F\u002F or using the extension\ncontext.router;\n\u002F\u002F adds a new entry to the pages stack\nrouter.push(const BooksListRoute());\n\u002F\u002F or by using paths\nrouter.pushPath('\u002Fbooks');\n\u002F\u002F removes last entry in stack and pushes provided route\n\u002F\u002F if last entry == provided route page will just be updated\nrouter.replace(const BooksListRoute());\n\u002F\u002F or by using paths\nrouter.replacePath('\u002Fbooks');\n\u002F\u002F pops until provided route, if it already exists in stack\n\u002F\u002F else adds it to the stack (good for web Apps).\nrouter.navigate(const BooksListRoute());\n\u002F\u002F or by using paths\nrouter.navigatePath('\u002Fbooks');\n\u002F\u002F on Web it calls window.history.back();\n\u002F\u002F on Native it navigates you back\n\u002F\u002F to the previous location\nrouter.back();\n\u002F\u002F adds a list of routes to the pages stack at once\nrouter.pushAll([\n  BooksListRoute(),\n  BookDetailsRoute(id: 1),\n]);\n\u002F\u002F This is like providing a completely new stack as it rebuilds the stack\n\u002F\u002F with the list of passed routes\n\u002F\u002F entries might just update if already exist\nrouter.replaceAll([\n  LoginRoute(),\n]);\n\n\u002F\u002F pops the top page even if it's the last entry in stack\ncontext.router.pop()\n\u002F\u002F pops the most top page of the most top router even if it's the last entry in stack\ncontext.router.popTop();\n\n\u002F\u002F pops the last page unless blocked or stack has only 1 entry\ncontext.router.maybePop();\n\u002F\u002F pops the most top page of the most top router unless blocked\n\u002F\u002F or stack has only 1 entry\ncontext.router.maybePopTop();\n\u002F\u002F keeps popping routes until predicate is satisfied\ncontext.router.popUntil((route) => route.settings.name == 'HomeRoute');\n\u002F\u002F a simplified version of the above line\ncontext.router.popUntilRouteWithName('HomeRoute');\n\u002F\u002F keeps popping routes until route with provided path is found\ncontext.router.popUntilRouteWithPath('\u002Fsome-path');\n\u002F\u002F pops all routes down to the root\ncontext.router.popUntilRoot();\n\u002F\u002F removes the top most page in stack even if it's the last\n\u002F\u002F remove != pop, it doesn't respect WillPopScopes it just\n\u002F\u002F removes the entry.\ncontext.router.removeLast();\n\u002F\u002F removes any route in stack that satisfies the predicate\n\u002F\u002F this works exactly like removing items from a regular List\n\u002F\u002F \u003CPageRouteInfo>[...].removeWhere((r)=>)\ncontext.router.removeWhere((route) => );\n\u002F\u002F you can also use the common helper methods from context extension to navigate\ncontext.pushRoute(const BooksListRoute());\ncontext.replaceRoute(const BooksListRoute());\ncontext.navigateTo(const BooksListRoute());\ncontext.navigateToPath('\u002Fbooks');\ncontext.back();\ncontext.maybePop();\ncontext.pop();\n```\n\n## Passing Arguments\n\nThat's the fun part! **AutoRoute** automatically detects and handles your page arguments for you, the generated route object will deliver all the arguments your page needs including path\u002Fquery params.\n\ne.g. The following page widget will take an argument of type `Book`.\n\n```dart\n@RoutePage()\nclass BookDetailsPage extends StatelessWidget {\n  const BookDetailsPage({required this.book});\n\n  final Book book;\n  ...\n```\n\n**Note:** Default values are respected. Required fields are also respected and handled properly.\n\nThe generated `BookDetailsRoute` will deliver the same arguments to its corresponding page.\n\n```dart\nrouter.push(BookDetailsRoute(book: book));\n```\n\n**Note:** All arguments are generated as named parameters regardless of their original type.\n\n## Returning Results\n\nYou can return results by either using the pop completer or by passing a callback function as an argument the same way you'd pass an object.\n\n#### 1. Using the `pop` completer\n\n```dart\nvar result = await router.push(LoginRoute());\n```\n\nthen inside of your `LoginPage`, pop with results\n\n```dart\nrouter.maybePop(true);\n```\n\nSpecifying the type of the result is optional, but it's recommended to avoid runtime errors.\n\n```dart\nvar result = await router.push\u003Cbool>(LoginRoute());\n```\n\nand of course we pop with the same type\n\n```dart\nrouter.maybePop\u003Cbool>(true);\n```\n\n#### 2. Passing a callback function as an argument.\n\nWe only have to add a callback function as a parameter to our page constructor like follows:\n\n```dart\n@RoutePage()\nclass BookDetailsPage extends StatelessWidget {\n  const BookDetailsRoute({this.book, required this.onRateBook});\n\n  final Book book;\n  final void Function(int) onRateBook;\n  ...\n```\n\nThe generated `BookDetailsRoute` will deliver the same arguments to its corresponding page.\n\n```dart\ncontext.pushRoute(\n  BookDetailsRoute(\n    book: book,\n    onRateBook: (rating) {\n      \u002F\u002F handle result\n    },\n  ),\n);\n```\n\nIf you're finishing with results, make sure you call the callback function as you pop the page\n\n```dart\nonRateBook(RESULT);\ncontext.maybePop();\n```\n\n**Note:** Default values are respected. Required fields are also respected and handled properly.\n\n## Nested Navigation\n\nNested navigation means building an inner router inside of a page of another router, for example in the below diagram users page is built inside of dashboard page.\n\n\u003Cp align=\"center\">\n  \u003Cimg alt=\"nested-router-demo\"  src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fmaster\u002Fart\u002Fnested_router_demo.png?raw=true\" height=\"370\">\n\u003C\u002Fp>\n\nDefining nested routes is as easy as populating the children field of the parent route. In the following example `UsersPage`, `PostsPage` and `SettingsPage` are nested children of `DashboardPage`.\n\n```dart\n@AutoRouterConfig(replaceInRouteName: 'Page,Route')\nclass AppRouter extends RootStackRouter {\n\n@override\nList\u003CAutoRoute> get routes => [\n    AutoRoute(\n      path: '\u002Fdashboard',\n      page: DashboardRoute.page,\n      children: [\n        AutoRoute(path: 'users', page: UsersRoute.page),\n        AutoRoute(path: 'posts', page: PostsRoute.page),\n        AutoRoute(path: 'settings', page: SettingsRoute.page),\n      ],\n    ),\n    AutoRoute(path: '\u002Flogin', page: LoginRoute.page),\n  ];\n}\n```\n\nTo render\u002Fbuild nested routes we need an `AutoRouter` widget that works as an outlet or a nested router-view inside of our dashboard page.\n\n```dart\nclass DashboardPage extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      children: [\n        Column(\n          children: [\n            NavLink(label: 'Users', destination: const UsersRoute()),\n            NavLink(label: 'Posts', destination: const PostsRoute()),\n            NavLink(label: 'Settings', destination: const SettingsRoute()),\n          ],\n        ),\n        Expanded(\n          \u002F\u002F nested routes will be rendered here\n          child: AutoRouter(), \u002F\u002F this is important\n        ),\n      ],\n    );\n  }\n}\n```\n\n**Note** NavLink is just a button that calls router.push(destination). Now if we navigate to `\u002Fdashboard\u002Fusers`, we will be taken to the `DashboardPage` and the `UsersPage` will be shown inside of it.\n\nWhat if want to show one of the child pages at `\u002Fdashboard`? We can simply do that by giving the child routes an empty path `''` to make initial or by setting initial to true.\n\n```dart\nAutoRoute(\n  path: '\u002Fdashboard',\n  page: DashboardRoute.page,\n  children: [\n    AutoRoute(path: '', page: UsersRoute.page),\n    AutoRoute(path: 'posts', page: PostsRoute.page),\n  ],\n)\n```\n\nor by using a `RedirectRoute`\n\n```dart\nAutoRoute(\n  path: '\u002Fdashboard',\n  page: DashboardRoute.page,\n  children: [\n    RedirectRoute(path: '', redirectTo: 'users'),\n    AutoRoute(path: 'users', page: UsersRoute.page),\n    AutoRoute(path: 'posts', page: PostsRoute.page),\n  ],\n)\n```\n\n#### Creating Empty Shell routes\n\nEmpty shell routes build a screen that contain the `AutoRouter` widget, which is used to render nested routes.\nSo you can build the widget your self like follows:\n\n```dart\n@RoutePage()\nclass MyShellPage extends StatelessWidget {\n  const MyShellPage({Key? key}) : super(key: key);\n  @override\n  Widget build(BuildContext context) {\n     \u002F\u002F\u002F you can wrap the AutoRouter with any widget you want\n    return  AutoRouter();\n  }\n}\n```\n\nYou can shorten the code above a bit by directly extending the `AutoRouter` Widget.\n\n```dart\n@RoutePage()\nclass MyShellPage extends AutoRouter {\n   const MyShellPage({Key? key}) : super(key: key);\n}\n```\n\nfinally you can create a shell route without code generation using the `EmptyShellRoute` helper\n\n```dart\n   const BooksTab = EmptyShellRoute('BooksTab');\n   context.push(BooksTab());\n```\n\n### Things to keep in mind when implementing nested navigation\n\n1. Each router manages its own pages stack.\n2. Navigation actions like push, pop and friends are handled by the topmost router and bubble up if it couldn't be handled.\n\n## Tab Navigation\n\nIf you're working with flutter mobile, you're most likely to implement tabs navigation, that's why `auto_route` makes tabs navigation as easy and straightforward as possible.\n\nIn the previous example we used an `AutoRouter` widget to render nested child routes, `AutoRouter` is just a shortcut for `AutoStackRouter`. `StackRouters` manage a stack of pages inside of them, where the active\u002Fvisible page is always the one on top and you'd need to pop it to see the page beneath it.\n\nNow we can try to implement our tabs using an `AutoRouter` (StackRouter) by pushing or replacing a nested route every time the tab changes and that might work, but our tabs state will be lost, not to mention the transition between tabs issue, luckily auto_route comes equipped with an `AutoTabsRouter`, which is especially made to handle tab navigation.\n\n`AutoTabsRouter` lets you switch between different routes while preserving offstage-routes state, tab routes are lazily loaded by default (can be disabled) and it finally allows to create whatever transition animation you want.\n\nLet's change the previous example to use tab navigation.\n\nNotice that we're not going to change anything in our routes declaration map, we still have a dashboard page that has three nested children: users, posts and settings.\n\n```dart\nclass DashboardPage extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    return AutoTabsRouter(\n      \u002F\u002F list of your tab routes\n      \u002F\u002F routes used here must be declared as children\n      \u002F\u002F routes of \u002Fdashboard\n      routes: const [\n        UsersRoute(),\n        PostsRoute(),\n        SettingsRoute(),\n      ],\n      transitionBuilder: (context,child,animation) => FadeTransition(\n            opacity: animation,\n            \u002F\u002F the passed child is technically our animated selected-tab page\n            child: child,\n          ),\n      builder: (context, child) {\n        \u002F\u002F obtain the scoped TabsRouter controller using context\n        final tabsRouter = AutoTabsRouter.of(context);\n        \u002F\u002F Here we're building our Scaffold inside of AutoTabsRouter\n        \u002F\u002F to access the tabsRouter controller provided in this context\n        \u002F\u002F\n        \u002F\u002F alternatively, you could use a global key\n        return Scaffold(\n          body: child,\n          bottomNavigationBar: BottomNavigationBar(\n            currentIndex: tabsRouter.activeIndex,\n            onTap: (index) {\n              \u002F\u002F here we switch between tabs\n              tabsRouter.setActiveIndex(index);\n            },\n            items: [\n              BottomNavigationBarItem(label: 'Users', ...),\n              BottomNavigationBarItem(label: 'Posts', ...),\n              BottomNavigationBarItem(label: 'Settings', ...),\n            ],\n          ),\n        );\n      },\n    );\n  }\n}\n```\n\nIf you think the above setup is a bit messy you could use the shipped-in `AutoTabsScaffold` that makes things much cleaner.\n\n```dart\nclass DashboardPage extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return AutoTabsScaffold(\n      routes: const [\n        UsersRoute(),\n        PostsRoute(),\n        SettingsRoute(),\n      ],\n      bottomNavigationBuilder: (_, tabsRouter) {\n        return BottomNavigationBar(\n          currentIndex: tabsRouter.activeIndex,\n          onTap: tabsRouter.setActiveIndex,\n          items: const [\n            BottomNavigationBarItem(label: 'Users', ...),\n            BottomNavigationBarItem(label: 'Posts', ...),\n            BottomNavigationBarItem(label: 'Settings', ...),\n          ],\n        );\n      },\n    );\n  }\n}\n```\n\n### Using PageView\n\nUse the `AutoTabsRouter.pageView` constructor to implement tabs using PageView\n\n```dart\nAutoTabsRouter.pageView(\n  routes: [\n    BooksTab(),\n    ProfileTab(),\n    SettingsTab(),\n  ],\n  builder: (context, child, _) {\n    final tabsRouter = AutoTabsRouter.of(context);\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(context.topRoute.name),\n        leading: AutoLeadingButton(),\n      ),\n      body: child,\n      bottomNavigationBar: BottomNavigationBar(\n        currentIndex: tabsRouter.activeIndex,\n        onTap: tabsRouter.setActiveIndex,\n        items: [\n          BottomNavigationBarItem(label: 'Books', ...),\n          BottomNavigationBarItem(label: 'Profile', ...),\n          BottomNavigationBarItem(label: 'Settings', ...),\n        ],\n      ),\n    );\n  },\n);\n```\n\n### Using TabBar\n\nUse the `AutoTabsRouter.tabBar` constructor to implement tabs using TabBar\n\n```dart\nAutoTabsRouter.tabBar(\n  routes: [\n    BooksTab(),\n    ProfileTab(),\n    SettingsTab(),\n  ],\n  builder: (context, child, controller) {\n    final tabsRouter = AutoTabsRouter.of(context);\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(context.topRoute.name),\n        leading: AutoLeadingButton(),\n        bottom: TabBar(\n          controller: controller,\n          tabs: const [\n            Tab(text: '1', icon: Icon(Icons.abc)),\n            Tab(text: '2', icon: Icon(Icons.abc)),\n            Tab(text: '3', icon: Icon(Icons.abc)),\n          ],\n        ),\n      ),\n      body: child,\n      bottomNavigationBar: BottomNavigationBar(\n        currentIndex: tabsRouter.activeIndex,\n        onTap: tabsRouter.setActiveIndex,\n        items: [\n          BottomNavigationBarItem(label: 'Books',...),\n          BottomNavigationBarItem(label: 'Profile',...),\n          BottomNavigationBarItem(label: 'Settings',...),\n        ],\n      ),\n    );\n  },\n);\n```\n\n## Finding The Right Router\n\nEvery nested `AutoRouter` has its own routing controller to manage the stack inside of it and the easiest way to obtain a scoped controller is by using the `BuildContext`.\n\nIn the previous example, `DashboardPage` is a root level stack entry so calling `AutoRouter.of(context)` anywhere inside of it will get us the root routing controller.\n\n`AutoRouter` widgets that are used to render nested routes, insert a new router scope into the widgets tree, so when a nested route calls for the scoped controller, they will get the closest parent controller in the widgets tree; not the root controller.\n\n```dart\nclass Dashboard extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    \u002F\u002F this will get us the root routing controller\n    AutoRouter.of(context);\n    return Scaffold(\n      appBar: AppBar(title: Text('Dashboard page')),\n      \u002F\u002F this inserts a new router scope into the widgets tree\n      body: AutoRouter()\n    );\n  }\n}\n```\n\nHere's a simple diagram to help visualize this\n\n\u003Cp align=\"center\">\n  \u003Cimg  alt=\"scoped-router-demo\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fmaster\u002Fart\u002Fscoped_routers_demo.png\" height=\"570\">\n\u003C\u002Fp>\n\nAs you can tell from the above diagram it's possible to access parent routing controllers by calling `router.parent\u003CT>()`, we're using a generic function because we have two different routing controllers: `StackRouter` and `TabsRouter`, one of them could be the parent controller of the current router and that's why we need to specify a type.\n\n```dart\nrouter.parent\u003CStackRouter>() \u002F\u002F this returns  the parent router as a Stack Routing controller\nrouter.parent\u003CTabsRouter>() \u002F\u002F this returns the parent router as a Tabs Routing controller\n```\n\nOn the other hand, obtaining the root controller does not require type casting because it's always a `StackRouter`.\n\n```dart\nrouter.root \u002F\u002F this returns the root router as a Stack Routing controller\n```\n\nYou can obtain access to inner-routers from outside their scope using a global key\n\n```dart\nclass DashboardPage extends StatefulWidget {\n  @override\n  _DashboardPageState createState() => _DashboardPageState();\n}\n\nclass _DashboardPageState extends State\u003CDashboardPage> {\n  final _innerRouterKey = GlobalKey\u003CAutoRouterState>();\n\n  @override\n  Widget build(BuildContext context) {\n    return Row(\n      children: [\n        Column(\n          children: [\n            NavLink(\n              label: 'Users',\n              onTap: () {\n                final router = _innerRouterKey.currentState?.controller;\n                router?.push(const UsersRoute());\n              },\n            ),\n            ...\n          ],\n        ),\n        Expanded(\n          child: AutoRouter(key: _innerRouterKey),\n        ),\n      ],\n    );\n  }\n}\n```\n\nYou could also obtain access to inner-routers from outside their scope without a global key, as long as they're initiated.\n\n```dart\n\u002F\u002F assuming this is the root router\ncontext.innerRouterOf\u003CStackRouter>(UserRoute.name);\n\u002F\u002F or if we're using an AutoTabsRouter inside of DashboardPage\ncontext.innerRouterOf\u003CTabsRouter>(UserRoute.name);\n```\n\nAccessing the `DashboardPage` inner router from the previous example.\n\n```dart\nclass Dashboard extends StatelessWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text('Dashboard'),\n        actions: [\n          IconButton(\n            icon: Icon(Icons.person),\n            onPressed: () {\n              \u002F\u002F accessing the inner router from\n              \u002F\u002F outside the scope\n              final router = context.innerRouterOf\u003CStackRouter>(DashboardRoute.name)\n              router?.push(const UsersRoute());\n            },\n          ),\n        ],\n      ),\n      body: AutoRouter(), \u002F\u002F we're trying to get access to this\n    );\n  }\n}\n```\n\n## Navigating Without Context\n\nTo navigate without context you can simply assign your generated router to a global variable\n\n```dart\n\u002F\u002F declare your route as a global variable\nfinal appRouter = AppRouter();\n\nclass MyApp extends StatefulWidget {\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp.router(\n      routerConfig: appRouter.config(),\n    );\n  }\n}\n```\n\n**Note:** Using global variable is not recommended and is considered bad practice and most of the times you should use dependency injection instead.\n\nHere's an example using `get_it` (which is just a personal favorite). You can use any dependency injection package you like.\n\n```dart\nvoid main(){\n  \u002F\u002F make sure you register it as a Singleton or a lazySingleton\n  getIt.registerSingleton\u003CAppRouter>(AppRouter());\n  runApp(MyApp());\n}\n\nclass MyApp extends StatefulWidget {\n  @override\n  Widget build(BuildContext context) {\n    final appRouter = getIt\u003CAppRouter>();\n\n    return MaterialApp.router(\n      routerConfig: appRouter.config(),\n    );\n  }\n}\n```\n\nNow you can access your router anywhere inside of your app without using context.\n\n```dart\ngetIt\u003CAppRouter>().push(...);\n```\n\n**Note:** Navigating without context is not recommended in nested navigation unless you use `navigate` instead of `push` and you provide a full hierarchy, e.g `router.navigate(SecondRoute(children: [SubChild2Route()]))`\n\n## Deep Linking\n\n**AutoRoute** will automatically handle deep-links coming from the platform, but native platforms require some setup, see [Deep linking topic](https:\u002F\u002Fdocs.flutter.dev\u002Fui\u002Fnavigation\u002Fdeep-linking) in flutter documentation.\n\n### Using Deep-link Transformer\n\nDeep link transformer intercepts deep-links before they're processed by the matcher, it's useful for stripping or modifying deep-links before they're matched.\n\nIn the following example we will strip a prefix from the deep-link before it's matched.\n\n```dart\nMaterialApp.router(\n  routerConfig: _appRouter.config(\n    deepLinkTransformer: (uri) {\n      if (uri.path.startsWith('\u002Fprefix')) {\n        return SynchronousFuture(\n        uri.replace(path: uri.path.replaceFirst('\u002Fprefix', '')),\n          );\n      }\n      return SynchronousFuture(uri);\n    }\n  ),\n);\n```\n\n**Note** for prefix stripping use the shipped-in `DeepLink.prefixStripper('prefix')`\n\n```dart\nMaterialApp.router(\n  routerConfig: _appRouter.config(\n    deepLinkTransformer: DeepLink.prefixStripper('prefix'),\n  ),\n);\n```\n\n### Using Deep-link Builder\n\nDeep link builder is an interceptor for deep-links where you can validate or override deep-links coming from the platform.\n\nIn the following example we will only allow deep-links starting with `\u002Fproducts`\n\n```dart\nMaterialApp.router(\n  routerConfig: _appRouter.config(\n    deepLinkBuilder: (deepLink) {\n      if (deepLink.path.startsWith('\u002Fproducts')) {\n        \u002F\u002F continue with the platform link\n        return deepLink;\n      } else {\n        return DeepLink.defaultPath;\n        \u002F\u002F or DeepLink.path('\u002F')\n        \u002F\u002F or DeepLink([HomeRoute()])\n      }\n    }\n  ),\n);\n```\n\n### Deep Linking to non-nested Routes\n\n**AutoRoute** can build a stack from a linear route list as long as they're ordered properly and can be matched as prefix, e.g `\u002F` is a prefix match of `\u002Fproducts`, and `\u002Fproducts` is prefix match of `\u002Fproducts\u002F:id`. Then we have a setup that looks something like this:\n\n- `\u002F`\n- `\u002Fproducts`\n- `\u002Fproducts\u002F:id`\n\nNow, receiving this deep-link `\u002Fproducts\u002F123` will add all above routes to the stack. This of course requires `includePrefixMatches` to be true in the root config (default is `!kWeb`) or when using `pushNamed`, `navigateNamed` and `replaceNamed`.\n\n**Things to keep in mind**:\n\n- If a full match can not finally be found, no prefix matches will be included.\n- Paths that require a full path match => `AutoRoute(path:'path', fullMatch: true)` will not be\n  included as prefix matches.\n- In the above example, if `\u002Fproducts\u002F:id` comes before `\u002Fproducts`, `\u002Fproducts` will not be\n  included.\n\n## Declarative Navigation\n\nTo use declarative navigation with auto_route, you simply use the `AutoRouter.declarative` constructor and return a list of routes based on state.\n\n```dart\nAutoRouter.declarative(\n  routes: (handler) => [\n    BookListRoute(),\n    if(_selectedBook != null) {\n      BookDetailsRoute(id: _selectedBook.id),\n    }\n  ],\n);\n```\n\n**Note:** The handler contains a temp-list of pending initial routes which can be read only once.\n\n## Working with Paths\n\nWorking with paths in **AutoRoute** is optional because `PageRouteInfo` objects are matched by name unless pushed as a string using the `deepLinkBuilder` property in root delegate or `pushNamed`, `replaceNamed` `navigateNamed` methods.\n\nIf you don’t specify a path it’s going to be generated from the page name e.g. `BookListPage` will have ‘book-list-page’ as a path, if initial arg is set to true the path will be `\u002F`, unless it's relative then it will be an empty string `''`.\n\nWhen developing a web application or a native app that requires deep-linking, you'd probably need to define paths with clear memorable names, and that's done using the `path` argument in `AutoRoute`.\n\n```dart\nAutoRoute(path: '\u002Fbooks', page: BookListPage),\n```\n\n### Path Parameters (dynamic segments)\n\nYou can define a dynamic segment by prefixing it with a colon\n\n```dart\nAutoRoute(path: '\u002Fbooks\u002F:id', page: BookDetailsPage),\n```\n\nThe simplest way to extract path parameters from path and gain access to them is by annotating constructor params with `@PathParam('optional-alias')` with the same alias\u002Fname of the segment.\n\n```dart\nclass BookDetailsPage extends StatelessWidget {\n  const BookDetailsPage({@PathParam('id') this.bookId});\n\n  final int bookId;\n  ...\n}\n```\n\nNow writing `\u002Fbooks\u002F1` in the browser will navigate you to `BookDetailsPage` and automatically extract the `bookId` argument from path and inject it to your widget.\n\n#### Inherited Path Parameters\n\nTo inherit a path-parameter from a parent route's path, we need to use `@PathParam.inherit` annotation in the child route's constructor. Let's say we have the following setup:\n\n```dart\nAutoRoute(\n  path: '\u002Fproduct\u002F:id',\n  page: ProductRoute.page,\n  children: [\n    AutoRoute(path: 'review',page: ProductReviewRoute.page),\n  ],\n)\n```\n\nNow `ProductReviewScreen` expects a path-param named `id` but, from the above snippet we know that the path corresponding with it. `review` has no path parameters, but we can inherit 'id' from the parent `\u002Fproduct\u002F:id` like follows:\n\n```dart\n@RoutePage()\nclass ProductReviewScreen extends StatelessWidget {\n  \u002F\u002F the path-param 'id' will be inherited and it can not be passed\n  \u002F\u002F as a route arg by user\n  const ProductReviewScreen({super.key, @PathParam.inherit('id') required String id});\n}\n```\n\n### Query Parameters\n\nQuery parameters are accessed the same way, simply annotate the constructor parameter to hold the value of the query param with `@QueryParam('optional-alias')` and let **AutoRoute** do the rest.\n\nYou could also access path\u002Fquery parameters using the scoped `RouteData` object.\n\n```dart\nRouteData.of(context).pathParams;\n\u002F\u002F or using the extension\ncontext.routeData.queryParams;\n```\n\n`Tip`: if your parameter name is the same as the path\u002Fquery parameter, you could use the const `@pathParam` or `@queryParam` and not pass a slug\u002Falias.\n\n```dart\n@RoutePage()\nclass BookDetailsPage extends StatelessWidget {\n  const BookDetailsPage({@pathParam this.id});\n\n  final int id;\n  ...\n}\n```\n\n### Redirecting Paths\n\nPaths can be redirected using `RedirectRoute`. The following setup will navigate us to `\u002Fbooks` when `\u002F` is matched.\n\n```dart\n\u003CAutoRoute> [\n  RedirectRoute(path: '\u002F', redirectTo: '\u002Fbooks'),\n  AutoRoute(path: '\u002Fbooks', page: BookListRoute.page),\n]\n```\n\nWhen redirecting initial routes the above setup can be simplified by setting the `\u002Fbooks` path as initial and **AutoRoute** will automatically generate the required redirect code for you.\n\n```dart\n\u003CAutoRoute> [\n  AutoRoute(path: '\u002Fbooks', page: BookListRoute.page, initial: true),\n]\n```\n\nYou can also redirect paths with params like follows:\n\n```dart\n\u003CAutoRoute> [\n  RedirectRoute(path: 'books\u002F:id', redirectTo: '\u002Fbooks\u002F:id\u002Fdetails'),\n  AutoRoute(path: '\u002Fbooks\u002F:id\u002Fdetails', page: BookDetailsRoute.page),\n]\n```\n\n**Note**: `RedirectRoutes` are fully matched.\n\n### Wildcards\n\n**AutoRoute** supports wildcard matching to handle invalid or undefined paths.\n\n```dart\nAutoRoute(\n  path: '*',\n  page: UnknownRoute.page,\n)\n\u002F\u002F it could be used with defined prefixes\nAutoRoute(\n  path: '\u002Fprofile\u002F*',\n  page: ProfileRoute.page,\n)\n\u002F\u002F or it could be used with RedirectRoute\nRedirectRoute(\n  path: '*',\n  redirectTo: '\u002F',\n)\n```\n\n**Note:** Be sure to always add your wildcards at the end of your route list because routes are matched in order.\n\n## Route Guards\n\nThink of route guards as middleware or interceptors, routes can not be added to the stack without going through their assigned guards. Guards are useful for restricting access to certain routes.\n\nWe create a route guard by extending `AutoRouteGuard` from the **AutoRoute** package and implementing our logic inside of the onNavigation method.\n\n```dart\nclass AuthGuard extends AutoRouteGuard {\n\n  @override\n  void onNavigation(NavigationResolver resolver, StackRouter router) {\n    \u002F\u002F the navigation is paused until resolver.next() is called with either\n    \u002F\u002F true to resume\u002Fcontinue navigation or false to abort navigation\n    if(authenticated) {\n      \u002F\u002F if user is authenticated we continue\n      resolver.next(true);\n    } else {\n        \u002F\u002F we redirect the user to our login page\n        \u002F\u002F tip: use resolver.redirectUntil to have the redirected route\n        \u002F\u002F automatically removed from the stack when the resolver is completed\n        resolver.redirectUntil(\n          LoginRoute(onResult: (success) {\n            \u002F\u002F if success == true the navigation will be resumed\n            \u002F\u002F else it will be aborted\n            resolver.next(success);\n          },\n        );\n      );\n    }\n  }\n}\n```\n\n**Important**: `resolver.next()` should only be called once.\n\nThe `NavigationResolver` object contains the guarded route which you can access by calling the property `resolver.route` and a list of pending routes (if there are any) accessed by calling `resolver.pendingRoutes`.\n\nNow we assign our guard to the routes we want to protect.\n\n```dart\nAutoRoute(\n  page: ProfileRoute.page,\n  guards: [AuthGuard()],\n);\n```\n\n#### Guarding all stack-routes\n\nYou can have all your stack-routes (non-tab-routes) go through a list of global guards by overriding the guards property inside your router class. Lets say you have an app with no public screens, we'd have a global guard that only allows navigation if the user is authenticated or if we're navigating to the LoginRoute.\n\n```dart\n@AutoRouterConfig()\nclass AppRouter extends RootStackRouter{\n\n  @override\n  late final List\u003CAutoRouteGuard> guards = [\n    AutoRouteGuard.simple((resolver, router) {\n        if(isAuthenticated || resolver.routeName == LoginRoute.name) {\n          \u002F\u002F we continue navigation\n          resolver.next();\n        } else {\n          \u002F\u002F else we navigate to the Login page so we get authenticated\n\n          \u002F\u002F tip: use resolver.redirectUntil to have the redirected route\n          \u002F\u002F automatically removed from the stack when the resolver is completed\n          resolver.redirectUntil(LoginRoute(onResult: (didLogin) => resolver.next(didLogin)));\n        }\n      },\n    ),\n    \u002F\u002F add more guards here\n  ];\n\n\u002F\u002F ..routes[]\n}\n```\n\n### Using a Reevaluate Listenable\n\nRoute guards can prevent users from accessing private pages until they're logged in, but auth state may change when the user is already navigated to the private page, to make sure private pages are only accessed by logged-in users all the time, we need a listenable that tells the router that the auth state has changed and you need to re-evaluate your stack.\n\nThe following auth provider mock will act as our re-valuate listenable\n\n```dart\nclass AuthProvider extends ChangeNotifier {\n  bool _isLoggedIn = false;\n\n  bool get isLoggedIn => _isLoggedIn;\n\n  void login() {\n    _isLoggedIn = true;\n    notifyListeners();\n  }\n\n  void logout() {\n    _isLoggedIn = false;\n    notifyListeners();\n  }\n}\n```\n\nWe simply pass an instance of our `AuthProvider` to `reevaluateListenable` inside of `router.config`\n\n```dart\nMaterialApp.router(\n  routerConfig: _appRouter.config(\n    reevaluateListenable: authProvider,\n  ),\n);\n```\n\nNow, every time `AuthProvider` notifies listeners, the stack will be re-evaluated and `AutoRouteGuard.onNavigation()`. Methods will be re-called on all guards\n\nIn the above example, we assigned our `AuthProvider` to `reevaluateListenable` directly, that's because `reevaluateListenable` takes a `Listenable` and AuthProvider extends `ChangeNotifier` which is a `Listenable`, if your auth provider is a stream you can use `reevaluateListenable: ReevaluateListenable.stream(YOUR-STREAM)`\n\n**Note**: When the Stack is re-evaluated, the whole existing hierarchy will be re-pushed, so if you want to stop re-evaluating routes at some point, use `resolver.resolveNext(\u003Coptions>)` which is like `resolver.next()` but with more options.\n\n```dart\n@override\nvoid onNavigation(NavigationResolver resolver, StackRouter router) async {\n  if (authProvider.isAuthenticated) {\n    resolver.next();\n  } else {\n    resolver.redirectUntil(\n      WebLoginRoute(\n        \u002F\u002F\u002F this part is optional if you're not using reevaluateListenable as this method will \n        \u002F\u002F\u002F be called again and if the condition is satisfied the resolver will be completed\n        onResult: (didLogin) {\n          \u002F\u002F\u002F stop re-pushing any pending routes after current\n          resolver.resolveNext(didLogin, reevaluateNext: false);\n        },\n      ),\n    );\n  }\n}\n```\n\n## Wrapping Routes\n\nIn some cases we want to wrap our screen with a parent widget, usually to provide some values through context, e.g wrapping your route with a custom `Theme` or a `Provider`. To do that, simply implement `AutoRouteWrapper`, and have wrappedRoute(context) method return (this) as the child of your wrapper widget.\n\n```dart\n@RoutePage()\nclass ProductsScreen extends StatelessWidget implements AutoRouteWrapper {\n\n  @override\n  Widget wrappedRoute(BuildContext context) {\n    return Provider(create: (ctx) => ProductsBloc(), child: this);\n  }\n  ...\n}\n```\n\n## Navigation Observers\n\nNavigation observers are used to observe when routes are pushed ,replaced or popped ..etc.\n\nWe implement an AutoRouter observer by extending an `AutoRouterObserver` which is just a `NavigatorObserver` with tab route support.\n\n```dart\nclass MyObserver extends AutoRouterObserver {\n\n  @override\n  void didPush(Route route, Route? previousRoute) {\n    print('New route pushed: ${route.settings.name}');\n  }\n\n \u002F\u002F only override to observer tab routes\n  @override\n  void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {\n    print('Tab route visited: ${route.name}');\n  }\n\n  @override\n  void didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) {\n    print('Tab route re-visited: ${route.name}');\n  }\n}\n```\n\nThen we pass our observer to the `\u003CrouterName>.config().` **Important:** Notice that `navigatorObservers` property is a builder function that returns a list of observers and the reason for that is a navigator observer instance can only be used by a single router, so unless you're using a single router or you don't want your nested routers to inherit observers, make sure navigatorObservers builder always returns fresh observer instances.\n\n```dart\nreturn MaterialApp.router(\n  routerConfig: _appRouter.config(\n    navigatorObservers: () => [MyObserver()],\n  ),\n);\n```\n\nThe following approach **won't** work if you have nested routers unless they don't inherit the observers.\n\n```dart\nfinal _observer = MyObserver();\nreturn MaterialApp.router(\n  routerConfig: _appRouter.config(\n    \u002F\u002F this should always return new instances\n    navigatorObservers: () => [_observer],\n  ),\n);\n```\n\nEvery nested router can have it's own observers and inherit it's parent's.\n\n```dart\nAutoRouter(\n  inheritNavigatorObservers: true, \u002F\u002F true by default\n  navigatorObservers:() => [list of observers],\n);\n\nAutoTabsRouter(\n  inheritNavigatorObservers: true, \u002F\u002F true by default\n  navigatorObservers:() => [list of observers],\n);\n```\n\nWe can also make a certain screen **route** aware by subscribing to an `AutoRouteObserver` (route not router).\n\nFirst we provide our `AutoRouteObserver` instance\n\n```dart\nreturn MaterialApp.router(\n  routerConfig: _appRouter.config(\n    navigatorObservers: () => [AutoRouteObserver()],\n  ),\n);\n```\n\nNext, we use an `AutoRouteAware` mixin which is a `RouteAware` mixin with tab support to provide the needed listeners, then subscribe to our `AutoRouteObserver`.\n\n```dart\nclass BooksListPage extends State\u003CBookListPage> with AutoRouteAware {\n  AutoRouteObserver? _observer;\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    \u002F\u002F RouterScope exposes the list of provided observers\n    \u002F\u002F including inherited observers\n    _observer = RouterScope.of(context).firstObserverOfType\u003CAutoRouteObserver>();\n    if (_observer != null) {\n      \u002F\u002F we subscribe to the observer by passing our\n      \u002F\u002F AutoRouteAware state and the scoped routeData\n      _observer.subscribe(this, context.routeData);\n    }\n  }\n\n @override\n  void dispose() {\n    super.dispose();\n    \u002F\u002F don't forget to unsubscribe from the\n    \u002F\u002F observer on dispose\n    _observer.unsubscribe(this);\n  }\n\n  \u002F\u002F only override if this is a tab page\n  @override\n  void didInitTabRoute(TabPageRoute? previousRoute) {}\n\n  \u002F\u002F only override if this is a tab page\n  @override\n  void didChangeTabRoute(TabPageRoute previousRoute) {}\n\n  @override\n  void didPopNext() {}\n\n  @override\n  void didPushNext() {}\n\n  @override\n  void didPush() {}\n\n  @override\n  void didPop() {}\n}\n```\n\n#### AutoRouteAwareStateMixin\n\nThe above code can be simplified using `AutoRouteAwareStateMixin`\n\n```dart\nclass BooksListPage extends State\u003CBookListPage> with AutoRouteAwareStateMixin\u003CBookListPage> {\n  \u002F\u002F only override if this is a tab page\n  @override\n  void didInitTabRoute(TabPageRoute? previousRoute) {}\n\n  \u002F\u002F only override if this is a tab page\n  @override\n  void didChangeTabRoute(TabPageRoute previousRoute) {}\n\n  \u002F\u002F only override if this is a stack page\n  @override\n  void didPopNext() {}\n\n  \u002F\u002F only override if this is a stack page\n  @override\n  void didPushNext() {}\n}\n```\n\n## Customizations\n\n##### MaterialAutoRouter | CupertinoAutoRouter | AdaptiveAutoRouter\n\n| Property                    | Default value         | Definition                                                                        |\n| --------------------------- | --------------------- | --------------------------------------------------------------------------------- |\n| replaceInRouteName [String] | Page&#124Screen,Route | Used to replace conventional words in generated route name (pattern, replacement) |\n\n## Custom Route Transitions\n\nTo use custom route transitions use a `CustomRoute` and pass in your preferences. The `TransitionsBuilder` function needs to be passed as a static\u002Fconst reference that has the same signature as the `TransitionsBuilder` function of the `PageRouteBuilder` class.\n\n```dart\nCustomRoute(\n  page: LoginRoute.page,\n  \u002F\u002F TransitionsBuilders class contains a preset of common transitions builders.\n  transitionsBuilder: TransitionsBuilders.slideBottom,\n  duration: Duration(milliseconds: 400),\n)\n```\n\n`Tip:` Override `defaultRouteType` in generated router to define global custom route transitions.\n\nYou can of course use your own transitionsBuilder function, as long as it has the same function signature. The function has to take in exactly one `BuildContext`, `Animation\u003CDouble>`, `Animation\u003CDouble>` and a child `Widget` and it needs to return a `Widget`. Typically, you would wrap your child with one of Flutter's transition widgets as follows:\n\n```dart\nCustomRoute(\n  page: ZoomInScreen,\n  transitionsBuilder:\n    (BuildContext context, Animation\u003Cdouble> animation, Animation\u003Cdouble> secondaryAnimation, Widget child) {\n      \u002F\u002F you get an animation object and a widget\n      \u002F\u002F make your own transition\n      return ScaleTransition(scale: animation, child: child);\n  },\n)\n```\n\n## Custom Route Builder\n\nYou can use your own custom route by passing a `CustomRouteBuilder` function to `CustomRoute' and implement the builder function the same way we did with the TransitionsBuilder function, the most important part here is passing the page argument to our custom route.\n\nmake sure you pass the return type \u003CT> to your custom route builder function.\n\n```dart\nCustomRoute(\n  page: CustomPage,\n  customRouteBuilder: \u003CT>(BuildContext context, Widget child, AutoRoutePage\u003CT> page) {\n    return PageRouteBuilder\u003CT>(\n      fullscreenDialog: page.fullscreenDialog,\n      \u002F\u002F this is important\n      settings: page,\n      pageBuilder: (_,__,___) => child,\n    );\n  },\n)\n```\n\n## Others\n\n### Including Micro\u002FExternal Packages\n\nTo include routes inside of a depended-on package, we generated the routes inside the micro package like normal, then either use the generated routes inside your main router individually,\nor declare them inside your micro router and merge them with the main router.\n\n```dart\n  final myMicroRouter = MyMicroRouter();\n\n  @override\n  List\u003CAutoRoute> get routes => [\n        AutoRoute(page: HomeRoute.page, initial: true),\n        \u002F\u002F\u002F use micro routes individually\n        AutoRoute(page: RouteFromMicroPackage.page),\n        \u002F\u002F\u002F or merge all routes from micro router\n        ...myMicroRouter.routes,\n      ];\n```\n\n`Tip:` You can add export `MyMicroRouter` to `app_router.dart`, so you only import `app_router.dart` inside of your code.\n\n```dart\n\u002F\u002F ...imports\nexport 'package:my_package\u002Fmy_micro_router.dart'\n@AutoRouterConfig()\nclass AppRouter extends RootStackRouter {}\n```\n\n## Configuring builders\n\nTo pass builder configuration to `auto_route_generator` we need to add `build.yaml` file next to `pubspec.yaml` if not already added.\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_route_generator:\n      # configs for @RoutePage() generator ...\n      auto_route_generator:auto_router_generator:\n      # configs for @AutoRouterConfig() generator ...\n```\n\n### Passing custom ignore_for_file rules\n\nYou can pass custom ignore_for_file rules to the generated router by adding the following:\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_router_generator:\n       options:\n         ignore_fore_file:\n           - custom_rule_1\n           - custom_rule_2\n```\n\n### Optimizing generation time\n\nThe first thing you want to do to reduce generation time, is specifying the files build_runner should process and we do that by using [globs](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fglob). Globs are kind of regex patterns with little differences that's used to match file names. **Note:** for this to work on file level you need to follow a naming convention\n\n```\nlet's say we have the following files tree\n├── lib\n│ ├── none_widget_file.dart\n│ ├── none_widget_file2.dart\n│ └── ui\n│ ├── products_screen.dart\n│ ├── products_details_screen.dart\n```\n\nBy default, the builder will process all of these files to check for a page with `@RoutePage()`\nannotation, we can help by letting it know what files we need processed, e.g only process the files\ninside the ui folder:\n**Note** (\\*\\*) matches everything including '\u002F';\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_route_generator:\n        generate_for:\n          - lib\u002Fui\u002F**.dart\n```\n\nLet's say you have widget files inside of the ui folder, but we only need to process files ending with `_screen.dart`\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_route_generator:\n        generate_for:\n          - lib\u002Fui\u002F**_screen.dart\n```\n\nNow only `products_screen.dart`, `products_details_screen.dart` will be processed\n\nThe same goes for `@AutoRouterConfig` builder\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_route_generator: # this for @RoutePage\n        generate_for:\n          - lib\u002Fui\u002F**_screen.dart\n      auto_route_generator:auto_router_generator: # this for @AutoRouterConfig\n        generate_for:\n          - lib\u002Fui\u002Frouter.dart\n```\n\n## Enabling cached builds\n\n**This is still experimental**\nWhen cached builds are enabled, **AutoRoute** will try to prevent redundant re-builds by analyzing whether the file changes has any effect on the extracted route info, e.g any changes inside of the build method should be ignored.\n\n**Note** Enable cached builds on both generators\n\n```yaml\ntargets:\n  $default:\n    builders:\n      auto_route_generator:auto_route_generator: # this for @RoutePage\n        options:\n          enable_cached_builds: true\n        generate_for:\n          - lib\u002Fui\u002F**_screen.dart\n      auto_route_generator:auto_router_generator: # this for @AutoRouterConfig\n        options:\n          enable_cached_builds: true\n        generate_for:\n          - lib\u002Fui\u002Frouter.dart\n```\n\n### AutoLeadingButton-BackButton\n\n`AutoLeadingButton` is **AutoRoute**'s replacement to the default BackButton to handle nested or parent stack popping. To use it, simply assign it to the `leading` property inside of `AppBar`\n\n```dart\nAppBar(\n  title: Text(context.topRoute.name),\n  leading: AutoLeadingButton(),\n)\n```\nyou can also use `AutoBackButton.builder` above your Scaffold for example to provide a nullable leading widget to prevent AppBar.leadingWidth when there's no leading to show\n\n```dart\nAutoBackButton.builder(\n  builder: (BuildContext context, Widget? leading) {\n    return  Scaffold(\n      appBar: AppBar(\n        title: Text(context.topRoute.name),\n        leading: leading,\n      ),\n    );\n  },\n)\n```\n\n### ActiveGuardObserver\n\n`ActiveGuardObserver` can notify you when a guard is being checked and what guard it is. This can be used to implement a loading indicator for example.\n\n```dart\nvar isLoading = false;\nvoid initState(){\n  final guardObserver = context.router.activeGuardObserver;\n\n  guardObserver.addListener(() {\n    setState((){\n      isLoading = guardObserver.guardInProgress;\n    });\n  });\n}\n```\n### Android Predictive Back\n`auto_route` **v10** supports Android predictive back, which is a feature that allows the user to take a peek at the previous route by swiping from the edge of the screen before committing to the back action.\n\nFor now this feature needs to be enabled in manifest file, it also has some flutter considerations, see [Android Predictive Back](https:\u002F\u002Fdocs.flutter.dev\u002Frelease\u002Fbreaking-changes\u002Fandroid-predictive-back) for more information.\n\nafter you've enabled the feature in your manifest file, you can simply create predictive-back enabled routes by setting `enablePredictiveBackGesture` to true in your route type, you can also provide a custom `predictiveBackPageTransitionsBuilder` to customize the transitions.\n\n```dart\n  RouteType.material(\n      enablePredictiveBackGesture: true,\n       \u002F\u002F optionally provide a custom transitions builder\n      predictiveBackPageTransitionsBuilder: (context,animation,secondaryAnimation,child) {\n       \u002F\u002F return preferred transitions\n      },\n    )\n```\n\nyou can enable predictive back for all routes by overriding `defaultRouteType` in your router, all route types that apply on android support this feature.\n\n## Examples\n\n- [Declarative Navigation](https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fblob\u002Fmaster\u002Fauto_route\u002Fexample\u002Flib\u002Fdeclarative\u002Fdeclarative.router.dart)\n- [Nested Navigation](https:\u002F\u002Fgithub.com\u002FMilad-Akarie\u002Fauto_route_library\u002Fblob\u002Fmaster\u002Fauto_route\u002Fexample\u002Flib\u002Fnested-navigation\u002Fnested_navigation.router.dart)\n\n### Support auto_route\n\nYou can support auto_route by liking it on Pub and staring it on Github, sharing ideas on how we can enhance a certain functionality or by reporting any problems you encounter and of course buying a couple coffees will help speed up the development process\n","auto_route_library 是一个用于 Flutter 的路由生成器。它支持自动代码生成，简化了应用内页面导航的配置过程，提供了包括深链接、嵌套路由和标签导航在内的多种高级功能。项目采用 Dart 语言编写，并且遵循 MIT 许可证，适合于需要构建复杂导航结构的 Flutter 应用场景中使用，能够显著提高开发效率并减少手动配置错误。",2,"2026-06-11 03:22:05","top_language"]