{"id":32305687,"url":"https://github.com/schabanbo/qlevar_router","last_synced_at":"2025-10-23T06:54:28.173Z","repository":{"id":40331912,"uuid":"320884169","full_name":"SchabanBo/qlevar_router","owner":"SchabanBo","description":"Manage you project Routes. Create nested routes. Simply navigation without context to your pages. Change only one sub widget in your page when navigating to new route.","archived":false,"fork":false,"pushed_at":"2025-04-22T10:27:44.000Z","size":13944,"stargazers_count":87,"open_issues_count":10,"forks_count":20,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-23T06:54:12.818Z","etag":null,"topics":["dart","flutter","nasted-navigation","navigation","navigation2","router"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SchabanBo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-12-12T17:20:07.000Z","updated_at":"2025-04-22T10:27:48.000Z","dependencies_parsed_at":"2023-11-12T12:37:14.147Z","dependency_job_id":"f2714d9b-61f9-4097-a716-156ff7e6df6d","html_url":"https://github.com/SchabanBo/qlevar_router","commit_stats":{"total_commits":274,"total_committers":19,"mean_commits":"14.421052631578947","dds":0.5072992700729927,"last_synced_commit":"749e9ae8438fc2ec73afbe5aae93dd59d81510cc"},"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"purl":"pkg:github/SchabanBo/qlevar_router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchabanBo%2Fqlevar_router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchabanBo%2Fqlevar_router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchabanBo%2Fqlevar_router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchabanBo%2Fqlevar_router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SchabanBo","download_url":"https://codeload.github.com/SchabanBo/qlevar_router/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SchabanBo%2Fqlevar_router/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280577134,"owners_count":26354072,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-23T02:00:06.710Z","response_time":142,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","nasted-navigation","navigation","navigation2","router"],"created_at":"2025-10-23T06:54:24.966Z","updated_at":"2025-10-23T06:54:28.162Z","avatar_url":"https://github.com/SchabanBo.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Qlevar Router (QR)\n\n[![likes](https://img.shields.io/pub/likes/qlevar_router?logo=dart)](https://pub.dev/packages/qlevar_router)\n[![popularity](https://img.shields.io/pub/popularity/qlevar_router?logo=dart)](https://pub.dev/packages/qlevar_router)\n[![pub points](https://img.shields.io/pub/points/qlevar_router?logo=dart)](https://pub.dev/packages/qlevar_router) \n[![codecov](https://codecov.io/gh/SchabanBo/qlevar_router/branch/master/graph/badge.svg?token=WF1RBRWTN1)](https://codecov.io/gh/SchabanBo/qlevar_router)\n[![HitCount](https://hits.dwyl.com/SchabanBo/qlevar_router.svg?style=flat-square)](https://pub.dev/packages/qlevar_router)\n\n\n- [Qlevar Router (QR)](#qlevar-router-qr)\n  - [Demo](#demo)\n    - [The example Projects](#the-example-projects)\n    - [The Samples Project](#the-samples-project)\n  - [Parameters](#parameters)\n    - [Path Parameters](#path-parameters)\n    - [Query Parameters](#query-parameters)\n    - [Hidden params](#hidden-params)\n    - [Params features](#params-features)\n  - [Middleware](#middleware)\n    - [global middleware](#global-middleware)\n  - [Priority](#priority)\n    - [redirectGuard](#redirectguard)\n    - [canPop](#canpop)\n    - [onMatch](#onmatch)\n    - [onEnter](#onenter)\n    - [onExit](#onexit)\n    - [OnExited](#onexited)\n  - [Nested navigators](#nested-navigators)\n    - [Saving pages state when navigating](#saving-pages-state-when-navigating)\n    - [PageAlreadyExistAction](#pagealreadyexistaction)\n  - [Observer](#observer)\n  - [Not found page](#not-found-page)\n  - [Deferred loading](#deferred-loading)\n  - [Page Transition](#page-transition)\n    - [Mix it up](#mix-it-up)\n    - [App Page Transition](#app-page-transition)\n  - [Temporary router](#temporary-router)\n    - [Example](#example)\n    - [Limitations](#limitations)\n  - [waiting for page result](#waiting-for-page-result)\n  - [Restoration management](#restoration-management)\n  - [Other features](#other-features)\n  - [Add or remove routes in run Time](#add-or-remove-routes-in-run-time)\n  - [Clean Structure](#clean-structure)\n  - [Testing](#testing)\n  - [Declarative routing](#declarative-routing)\n    - [QDRoute](#qdroute)\n    - [How Declarative router works](#how-declarative-router-works)\n  - [Remove Url Hashtag](#remove-url-hashtag)\n  - [Web hot reload](#web-hot-reload)\n  - [Articles](#articles)\n  - [Projects](#projects)\n  - [Well tested](#well-tested)\n  - [Contribute](#contribute)\n\nQlevar router is a flutter package to help you with managing your project routing, navigation, deep linking, route params, etc ...\nWith Navigator 2.0 Manage your project routes and create nested routes. Change only one widget on your page when navigating to the new route. Navigate without context from anywhere to anywhere.\n\n```dart\n// Define your routes\nclass AppRoutes {\n  static String homePage = 'Home Page';\n  static String userPage = 'User Page';\n  final routes = [\n    QRoute(name: homePage, path: '/', builder: () =\u003e HomePage()),   \n    QRoute(\n        name: userPage,\n        path: '/user/:userId',\n        builder: () =\u003e HomePage(),\n        children: [\n          QRoute(name: homePage, path: '/settings', builder: () =\u003e SettingsPage()),\n          QRoute(name: homePage, path: '/profile', builder: () =\u003e ProfilePage()),\n        ]),\n    QRoute(path: '/products/:category(\\w)', builder: () =\u003e ProductCategory()),\n    QRoute(path: '/products/:id((^[0-9]\\$))', builder: () =\u003e ProductDetails()),\n  ];\n}\n\n// Create your app\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) =\u003e MaterialApp.router(\n      routeInformationParser: QRouteInformationParser(),\n      routerDelegate: QRouterDelegate(AppRoutes().routes));\n}\n\n// from anywhere in your code navigate to new page with\nQR.toName(AppRoutes.userPage, params:{'userId':2});\n// or\nQR.to('/user/2');\n\nQR.to('/user/6/profile')  // Here the Stack will be HomePage -\u003e ProfilePage()\nQR.to('products/456')     // Will call ProductDetails page\nQR.to('products/garden')  // Will call ProductCategory page\nQR.back()                 // Go back to the last page(in this case 'products/456')\nQR.currentPath            // will show the current path \n```\n\nyou want to work with the basic functions from the navigator just set which navigator to use with `QR.activeNavigatorName` and then call it and use it\n\n```dart\n  QR.navigator.canPop;\n  QR.navigator.currentRoute //Get the current route for this navigator\n\n  QR.navigator.pushName(String name, {Map\u003cString, dynamic\u003e? params})\n  QR.navigator.push(String path);\n\n  QR.navigator.replaceAll(String path);\n  QR.navigator.replaceAllWithName(String name, {Map\u003cString, dynamic\u003e? params});\n\n  QR.navigator.replace(String path, String withPath);\n  QR.navigator.removeLast();\n```\n\nor just call the navigator `QR.navigatorOf('Dashboard')`.\n**Note:** The routes name must be unique for each route in the app.\n\nUse these functions to see your navigators and Stack history and active pages in your project for a better understanding of where you are in your project and how to order your pages.\n\n```dart\nQR.getActiveTree() // Will show you a dialog contains the tree of the active navigator and pages\nQR.history.debug() // will show you a dialog contains the history stack for your current page.\n```\n\n## Demo\n\n### The example Projects\n\nPlease check out the demo of Qlevar Router [here](https://qlevar-router.netlify.app) to see how it works. You can find the code for this in [here](https://github.com/SchabanBo/qlevar_router/tree/master/example/lib)\n\nThe demo includes the following pages:\n\n- [Store](https://qlevar-router.netlify.app/#/store): A simple page navigation example with passing parameters through the URL.\n- [Dashboard](https://qlevar-router.netlify.app/#/dashboard): An example of authentication middleware and nested routes with sidebar.\n- [MobileStore](https://qlevar-router.netlify.app/#/mobile/stores):  An example of a bottom navigation bar.\n- [Middleware](https://qlevar-router.netlify.app/#/parent):  An example of different methods for the [middleware.](#middleware).\n- [Declarative](https://qlevar-router.netlify.app/#/declarative): An example of navigating based on the state of an object.\n- [EditableRoutes](https://qlevar-router.netlify.app/#/editable-routes/child): An example of adding or removing routes from the routes tree during runtime.\n\n### The Samples Project\n\nIn addition to the demo, you can find more samples and test some use cases in the [samples project](https://github.com/SchabanBo/qr_samples) Here are a few examples:\n\n- [Dashboard with splash page Example](https://github.com/SchabanBo/qr_samples/blob/main/lib/common_cases/dashboard.dart)\n- [Bottom Navigation bar Example](https://github.com/SchabanBo/qr_samples/blob/main/lib/common_cases/bottom_nav_bar.dart)\n- [TabView Example](https://github.com/SchabanBo/qr_samples/blob/main/lib/common_cases/tab_view.dart)\n- [NavRail Example](https://github.com/SchabanBo/qr_samples/blob/main/lib/common_cases/nav_rail.dart)\n\n## Parameters\n\nsend your params with the route. The params could be any object type.\n\n### Path Parameters\n\n```dart\nQRoute(path: '/:orderId',page: (child) =\u003e OrderDetails()),\n\n// User regex to define what this parameter can be\n// like this parameters can only be numbers\nQRoute(path: '/:id(^[0-9]+\\$)', builder: () =\u003e Text('Case 2')), \n\n// and this receive it in your page\nfinal orderId = QR.params['orderId'].toString()\n```\n\n### Query Parameters\n\n```dart\n QR.to('/home/items/details?itemName=${e.name}\u0026numbers=[2,6,7]')\n\n// and this receive it in your page\nfinal itemName = QR.params['itemName'].toString()\nfinal numbers = QR.params['numbers']\n```\n\n### Hidden params\n\nif you want to pass data between the page without showing it in the URL, you can use `QR.params.addAsHidden` this will add values to use after one page navigation, in the next time you navigate to a new page the param will be cleaned up. to keep it for more time, set the cleanUpAfter parameter.\n\n**NOTE:** using this on web will cause the data to be lost if the user refreshes the page.\n\n```dart\nQR.params.addAsHidden('param3', true, cleanUpAfter: 2);\n```\n\n### Params features\n\n- **keepAlive**: by default, the param will be deleted when navigating to a new route that does not contain it, so if you don't what to delete it in this case set this property to true, and *the package will not delete **as long as** this property is true*\n- **onChange**: set function to be called when this param will be changed it gives the current value and the new value\n- **onDelete**: set function to be called when this param will be deleted\n- **asInt**: Will return the value as int?\n- **asDouble**: Will return the value as double\n- **valueAs\u003cT\u003e**: Will return the value as the given type\n- **cleanupAfter**: set `cleanupAfter` so the package will clean up this param after X callas, where x is the number you set in `cleanupAfter`, a call could be push, replace, pop or to\n\n## Middleware\n\nwith middleware, you can set custom actions to run with the different event when you navigate\nto define them add `QMiddlewareBuilder` or a custom class that extends 'QMiddleware' them in your route, they will be called in the same order they are defined in.\n\n```dart\n QRoute(\n    path: '/home',\n    builder: () {\n      return HomePage();\n    },\n    middleware: [\n      QMiddlewareBuilder(\n          onEnterFunc: () =\u003e print('-- Enter Parent page --'),\n          onExitFunc: () =\u003e print('-- Exit Parent page --'),\n          onMatchFunc: () =\u003e print('-- Parent page Matched --')),\n      AuthMiddleware(),\n    ])\n\nclass AuthMiddleware extends QMiddleware{\n  final dataStorage = // Get you Data storage\n  @override\n  bool canPop() =\u003e dataStorage.canLeave;\n  @override\n  Future\u003cString?\u003e redirectGuard(String path) async =\u003e dataStorage.isLoggedIn ? null: '/parent/child-2';\n}\n```\n\n![Middleware](./example/assets/middleware.png)\n\n### global middleware\n\nyou can add global middleware to run on *every* route, to do that add the middlewares to `QR.settings.globalMiddleware`\n\n```dart\nQR.settings.globalMiddleware = [\n  QMiddlewareBuilder(\n      onEnterFunc: () =\u003e print('-- Enter Parent page --'),\n      onExitFunc: () =\u003e print('-- Exit Parent page --'),\n      onMatchFunc: () =\u003e print('-- Parent page Matched --')),\n  AuthMiddleware(),\n];\n```\n\n## Priority\n\nThe priority of the middleware, the lower the number the higher the priority. Middleware with the same priority will be executed in the order they were added\nMiddleware with higher priority will be executed first. The default priority is 500.\n\n```dart\nQRoute(\n    path: '/home',\n    builder: () =\u003e HomePage(),\n    middleware: [\n      QMiddlewareBuilder(\n          priority: 1,\n          onEnterFunc: () async =\u003e print('-- Enter Parent page --'),\n          onExitFunc: () async =\u003e print('-- Exit Parent page --'),\n          onMatchFunc: () async =\u003e print('-- Parent page Matched --')),\n      QMiddlewareBuilder(\n          priority: 2,\n          onEnterFunc: () async =\u003e print('-- Enter Parent page --'),\n          onExitFunc: () async =\u003e print('-- Exit Parent page --'),\n          onMatchFunc: () async =\u003e print('-- Parent page Matched --')),\n    ])\n```\n\n### redirectGuard\n\nyou can redirect to a new page whenever a page is called using the `redirectGuard`.\n\nThe `redirectGuard` gives the path as a parameter and takes the new path to redirect to.\nor it takes `null` so the page can be accessed.\n\n### canPop\n\ncan this route pop, called when trying to remove the page.\n\n### onMatch\n\nThis method will be called *every time* a path match it.\n  \n### onEnter\n\nThis method will be called before adding the page to the stack and before the page building\n\n### onExit\n\nThis method will be called before removing the page from the stack\n\n### OnExited\n\nThis method will be called one frame after the page was removed from the stack, this will be the best place to cleanup any resource that the page was using.\n\n## Nested navigators\n\nyou can nest navigators inside each other, to do that you need to add `QRoute.withChild` to your routes tree.\n\n```dart\nQRoute.withChild(\n  name: store,\n  path: '/store',\n  builderChild: (router) =\u003e StorePage(router),\n  initRoute: '/orders',\n  children: [\n    QRoute(name: items, path: '/items', builder: () =\u003e ItemsPage()),\n    QRoute(name: orders, path: '/orders', builder: () =\u003e OrderPage()),\n  ],\n);\n```\n\nthe `builderChild` will be called with the child navigator as a parameter, which is just a widget you can place on any place on the screen where you want to show the children of this route.\nsee the example for more details. \n[routes definition](https://github.com/SchabanBo/qlevar_router/blob/master/example/lib/routes/dashboard_routes.dart)\n[widget definition](https://github.com/SchabanBo/qlevar_router/tree/master/example/lib/pages/dashboard)\n\n### Saving pages state when navigating\n\nif you want to save the state of the pages when navigating between them, you have tow options:\n\n1- when navigating between the pages, set `pageAlreadyExistAction` to 'bringToTop', this will bring the page to the top of the stack. if this page has any child pages, they will be reordered to be on top of the stack the same way as they were before.\n2- you can use 'QR.navigatorOf(DashboardRoutes.dashboard).switchTo('home');' to switch between the pages, this will keep the state of the pages. This will internally set `pageAlreadyExistAction` to 'bringToTop'.\n\n### PageAlreadyExistAction\n\nThis parameter will be used when you navigate to a page that already exists in the stack.\n\n- **bringToTop**: will bring the page to the top of the stack, if the page has any child pages, they will be reordered to be on top of the stack the same way as they were before. see #61\n*NOTE:* this will not work for the route with `/` path\n- **IgnoreChildrenAndBringToTop**: will bring just the page to the top of the stack, if the page has any child pages, they will be ignored.\n- **remove**: if the page already exists, this will remove all pages on the top  until the page is on the top of the stack. \n\n## Observer\n\nTo set your observers to the navigators for the root navigator you need to pass them to `QRouterDelegate`\n\n```dart\nRouterDelegate(\n  appRoutes.routes,\n  observers: [\n    // Your observers\n  ],\n),\n```\n\nfor nested navigators you can pass them when defining a nested route\n\n```dart\nQRoute.withChild(\n  path: '/editable-routes',\n  builderChild: (child) =\u003e AddRemoveRoutes(child),\n  observers: [\n    // Add your observer for this navigator\n  ],\n  children: [..],\n);\n```\n\nto set global observes for every navigation in your app you could set QObserver to the `QR.observer`.\nQObserver can have :\n\n- **onNavigate**: add listener to every new route that will be added to the tree\n- **onPop**: Add listener to every route that will be deleted from the tree\n\n## Not found page\n\nyou can set your custom not found page to show it whenever a page was not found, or a default one will be set.\n\n```dart\n  QR.settings.notFoundPage = QRoute(path: '/404', builder: ()=\u003e NotFoundPage())\n```\n\n##  Deferred loading\nFor web application you can split your compiled java script files to moe than one for helping with the page loading speed. read more about it [here](https://medium.com/@SchabanBo/reduce-your-flutter-web-app-loading-time-8018d8f442)\n\n## Page Transition\n\nTo chose the Transition for your page set the `QRoute.pageType` to the of the types:\n\n- **QPlatformPage**: This type will be set as *QCupertinoPage* on IOS devices otherwise it will be *QMaterialPage*.\n- **QMaterialPage**: It will use the default MaterialRouteTransition\n- **QCupertinoPage**: It will use the default CupertinoRouteTransition\n- **QCustomPage**: to define a custom transition for your page.\n- **QSlidePage**: a predefined slide transition\n- **QFadePage**: a predefined fade transition\n\n### Mix it up\n\nyou can mix the transition animation by setting the property `withType`\nso if you want to show slide and fade transition you can do\n\n```dart\nQRoute(\n      path: '/child',\n      pageType: QFadePage(\n          transitionDurationMilliseconds: 1000,\n          withType: QSlidePage(transitionDurationMilliseconds: 5000), // set the type to mix with\n          ),\n      builder: () =\u003e TextPage('Hi child 4')),\n]),\n```\n\nplease note that when you mix transitions the only the first transition duration will be used\nin this case `QFadePage.transitionDurationMilliseconds (1000)` will be used and `QSlidePage.transitionDurationMilliseconds (5000)` will be ignored\n\n**QPlatformPage**, **QMaterialPage** and **QCupertinoPage** CANNOT be mixed.\n\n### App Page Transition\n\nyou can define the Transition for all pages in the app with setting the page type in `QR.settings.pagesType`\n\n## Temporary router\n\nIt creates a new navigator that is only active while the widget is in view. This is useful for displaying nested routes within a modal or popup without affecting the main navigation stack. and  is automatically removed when the widget is removed from the tree, ensuring that there are no lingering navigators consuming resources.\n\nThe temporary routes are the same as those defined in the main application, ensuring consistency and reuse of route definitions.\n\n```dart\nfinal storeRoutes = StoreRoutes().route;\nshowModalBottomSheet(\n  context: context,\n  builder: (_) {\n    return TemporaryQRouter(\n      name: 'temp Store',\n      path: '/temp-store',\n      routes: [storeRoutes],\n      initPath: storeRoutes.path,\n    );\n  },\n);\n```\nThis example will show the store page in a bottom sheet. the same routes are defined in the main app routes. and the sane pages will be displayed.\n\nTo Check if the temporary router is active or not you can use `QR.navigator.isTemporary`.\n\n### [Example](https://github.com/SchabanBo/qr_samples/blob/main/lib/examples/temporary_router.dart)\n\n### Limitations\n- URL Navigation: The temporary router will not function if the user directly types the path into the URL. This limitation arises because the temporary router is designed to exist only within the widget tree and is destroyed when the widget is removed.\n\n## waiting for page result\n\nIf you need to wait for a result from another page, you can do so by setting `waitForResult` to true before navigating to the page.\nMore info: [example](https://qlevar-router.netlify.app/#/await-result) | [example code](example\\lib\\pages\\await_result\\await_result_view.dart) | [tests](test\\waiting_results_test.dart).\n\n```dart\n/// navigate to the page using\nfinal result = await QR.to\u003cString\u003e('/page', waitForResult: true);\n// or by name\nfinal result = await QR.toName\u003cString\u003e('page', waitForResult: true);\n\n/// in the page you want to get the result from\nQR.back('result');\n\n```\n\n## Restoration management\n\nTo enable the restoration in the app you need to set `QRouterDelegate.restorationScopeId` and set `QRoute.pageType.restorationId` to the route you want to restore.\nor you set `QR.settings.autoRestoration` to true and the package will set the restoration id for you.\n\n**Note:** don't forget to set `MartialApp.restorationScopeId`.\n\n\n## Other features\n\n- **NavigatorState**: if you want to set the navigator state in the app, you can do so by pass it to `QRouterDelegate.navkey` when creating the `RouterDelegate`.\n- **BuildContext**: you can get the current context from any where by calling `QR.context`. This will give the current context of the current navigator.\n- **InitPage**: The default page to show when the app starts until the first route is loaded. you can change it by setting `QR.settings.initPage` to the page you want to show.\n\n## Add or remove routes in run Time\n\nYou can add new routes or delete the existing route from the route tree dynamically while the app is running.\nJust chose which navigator you want to add the routes to and then call\n\n```dart\nfinal navigator = QR.rootNavigator; // to add routes to the root navigator\nfinal navigator = QR.navigatorOf('/dashboard') // or add the routes to the dashboard navigator\nnavigator.addRoutes([QRoute(path: '/payrolls', builder:()=\u003e PayrollsPage()]);\n// now the use can navigate to the payrolls page\nnavigator.removeRoutes(['/payrolls']);\n// now if the use navigate to the payrolls page he will get not found page\n```\n\n## Clean Structure\n\nYou can split your route definition into multiple files so the route tree doesn't get too messy, or if you work with multiple Teams so each team can have his tree definition.\n\n```dart\nclass StoreRoutes {\n  static const store = 'Store';\n  static const orders = 'Orders';\n  static const items = 'Items';\n\n  QRoute routes() =\u003e QRoute.withChild(\n          name: store,\n          path: '/store',\n          builderChild: (child) =\u003e StorePage(child),\n          initRoute: '/orders',\n          children: [\n            QRoute(name: items, path: '/items', builder: () =\u003e ItemsPage()),\n            QRoute(name: orders, path: '/orders', builder: () =\u003e OrderPage()),\n          ]);\n}\n\nclass HomeRoutes {\n  static const home = 'Home';\n  static const info = 'Info';\n  static const settings = 'Settings';\n\n  QRoute routes() =\u003e QRoute.withChild(\n          name: home,\n          path: '/store',\n          builderChild: (child) =\u003e StorePage(child),\n          initRoute: '/info',\n          children: [\n            QRoute(name: info, path: '/info', builder: () =\u003e InfoPage()),\n            QRoute(\n                name: settings,\n                path: '/settings',\n                builder: () =\u003e SettingsPage()),\n          ]);\n}\n\nclass AppRoutes {\n  static const app = 'App';\n  List\u003cQRoute\u003e routes() =\u003e [\n        QRoute.withChild(\n            name: app,\n            path: '/',\n            builderChild: (child) =\u003e AppPage(child),\n            initRoute: '/store',\n            children: [\n              StoreRoutes().routes(), // Add the Store routes to the app\n              HomeRoutes().routes(), // Add the Home routes to the app\n            ]),\n      ];\n}\n```\n\n## Testing\n\nFor easy testing you can use `RouteMock` so when navigating while testing you can give which widget should be shown.\nE.x: you have a widget that when clicking on in should navigate to the HomeView. In normal case to test this you should add QRouterDelegate so when clicking on the widget the test wont fail and show the page. But in this case we only what to test if this button send the request to package in a correct way.\n\n```dart\nvoid main() {\n  testWidgets('RouteMock', (tester) async {\n    // Set up env\n    QR.reset();\n    QR.settings.mockRoute = _RouteMock();\n    \n    // add widget and test it\n    await tester.pumpWidget(const MaterialApp(home: _TestWidget()));\n    await tester.pumpAndSettle();\n    await tester.tap(find.byIcon(Icons.done));\n    await tester.pumpAndSettle();\n    // check if the widget sent the request to qlevar_Router\n    expect(QR.currentPath, '/home');\n  });\n}\n\nclass _RouteMock extends RouteMock {\n  @override\n  String? mockName(String name) {\n    return null;\n  }\n\n  @override\n  QRoute? mockPath(String path) {\n    // Check if qlevar_router has received a request to go to the home page\n    // if so, then return empty page so the test can pass.\n    // if not, the test will fail, becurse you are trying to navigate without setting the QRouterDelegate\n    if (path == '/home') {\n      return QRoute(path: '/home', builder: () =\u003e const SizedBox.shrink());\n    }\n    return null;\n  }\n}\n\nclass _TestWidget extends StatelessWidget {\n  const _TestWidget({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: IconButton(\n        onPressed: () {\n          QR.to('/home');\n        },\n        icon: const Icon(Icons.done),\n      ),\n    );\n  }\n}\n``` \n\n## Declarative routing\n\nif you want to change a page automatically according to an object state with having to check the object every time and then send the user to the right page. you can do so with the declarative routing.\njust link the object to `QDeclarative` and define the route to go to with every object change a let the rest to`QDeclarative`.\n\nThis is very useful when you have an object with a lot of properties that users need to fill. instead of one page with a lot of TextInputs (what is boring), you can split your object properties through multiple pages. and the user can fill them as flow one page at a time.\n\n**Note**: you don't need to define these routes in the AppRoute routes.\n\nFirst you need to define the parent root of the declarative route as declarative. and here you will get a key you need to pass it to `QDeclarative`.\n\n```dart\n   QRoute.declarative(path: '/declarative',declarativeBuilder: (k) =\u003e DeclarativePage(k)),\n```\n\nThen in the `DeclarativePage` in the build function give as widget a `QDeclarative`\n\nThe `QDeclarative` required two parameters\n\n- routeKey: key you got from QRoute\n- builder: List of type `List\u003cQDRoute\u003e` of the route to return based on the state [See QDRoute](#qdroute)\n\n```dart\nQDeclarative(\n      routeKey: widget.dKey, // give the key you got from QRoute\n      builder: () =\u003e [ \n               name: 'Hungry',\n              builder: () =\u003egetQuestion((v) =\u003e state.loveCoffee = v, 'Do you love Coffee?'),\n              when: () =\u003e state.loveCoffee == null,\n              // when this route pop, if you want to get out of the declarative\n              // router give false as result so the router know that this\n              // function didn't processed the pop and process it\n              onPop: () =\u003e false,\n            ),\n            QDRoute(\n                name: 'Burger',\n                builder: () =\u003e getQuestion((v) =\u003e state.loveBurger = v, 'Do you love burger?'),\n                onPop: () =\u003e state.loveCoffee = null,\n                when: () =\u003e state.loveBurger == null),\n            QDRoute(\n                name: 'Pizza',\n                builder: () =\u003e getQuestion((v) =\u003e state.lovePizza = v, 'Do you love Pizza?'),\n                onPop: () =\u003e state.loveBurger = null,\n                when: () =\u003e state.lovePizza == null,\n                pageType: QSlidePage(offset: Offset(-1, 1))),\n            QDRoute(\n                name: 'Result',\n                builder: result,\n                onPop: () =\u003e state.lovePizza = null,\n                when: () =\u003e state.allSet)\n          ]));\n```\n\n### QDRoute\n\nThis route is used with `QDeclarative` to define the pages to show according to the object state:\n\n- **name:** this name will be used as a key to define the route.\n- **builder:** here you give the widget to show.\n- **when:** when should the page be shown. here you can set the condition that defines if the page should be added to the page list.\n- **onPop:** this function will be called when the user what to go back, this will be trigger with `QR.back`, android back button, and browser back button. if you want on a page to get out of the `QDeclarative` to the previews page in the normal router give this function a false as a result so the router knows that this page has not been processed and the router needs to process it.\n- **pageType**: [The page Transition](#page-transition)\n\n### How Declarative router works\n\nOn every rebuild on the page the `QDeclarative` will call `QDeclarative.builder` and get the ** the First page* the returns true from `QDRoute.when` and then checks if the page exists in the page list, if yes all pages above it will be removed so the page will be on the top and be shown. If no will add it on the top and the page will be shown.\n\n## Remove Url Hashtag\n\nIf you what to remove the hashtag from the URL place it in your main method\n\n```dart\nvoid main() {\n  QR.setUrlStrategy();\n  runApp(MyApp());\n}\n```\n\n*Note:* sometimes in release mode you could get an error when you remove the hashtag, to fix it please see [this](https://github.com/SchabanBo/qlevar_router/issues/10)\n\n## Web hot reload\n\nAccording to [This](https://github.com/flutter/flutter/issues/53041#issuecomment-828943000) the hot reload feature could take some time unit it is ready.\nSo I took this [idea](https://github.com/flutter/flutter/issues/53041#issuecomment-829044969) and I made a fake BrowserAddressBar. so we can develop our application on Windows or Linux or Android and still having the BrowserAddressBar. This bar will only appear on debug mode and when the platform is not web.\n\nTo activate it.\n\n```dart\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) =\u003e MaterialApp.router(\n      routeInformationParser: QRouteInformationParser(),\n      routerDelegate: QRouterDelegate(AppRoutes().routes, withWebBar: true));\n}\n```\n\n## Articles\n\n- [Building an app using Cubit and qlevar_router](https://medium.com/@SchabanBo/building-an-app-using-cubit-and-qlevar-router-481fba0f2349)\n- [Reduce your flutter web app loading time](https://medium.com/@SchabanBo/reduce-your-flutter-web-app-loading-time-8018d8f442)\n- [Implementing Deep Linking in Flutter Using the qlevar_router](https://medium.com/@SchabanBo/implementing-deep-linking-in-flutter-using-the-qlevar-router-package-9bde293e3fe0)\n- [TabBar and BottomNavigationBar in flutter with qlevar_router](https://medium.com/@SchabanBo/tabbar-and-bottomnavigationbar-in-flutter-with-qlevar-router-893b1c476134)\n- [Implementing Deep Linking in Flutter Using the qlevar_router Package](https://medium.com/@SchabanBo/implementing-deep-linking-in-flutter-using-the-qlevar-router-package-9bde293e3fe0)\n\n## Projects\n\n- [Localic](https://github.com/SchabanBo/localic): A local management application uses [riverpod](https://riverpod.dev) as state management.\n\n## Well tested\n\nThe package is well tested, and I will keep adding more tests to make sure the package is stable.\nAs a navigation package it is very important to test every single scenario to make sure the package is stable. because a small change in defining the route could lead to missing param or a wrong path in the url.\nThat is why beside the default tests, i add a [test](https://github.com/SchabanBo/qlevar_router/tree/master/test) for every single scenario i got from the users. you can see the tests in the [scenario-code](https://github.com/SchabanBo/qlevar_router/tree/master/test/scenario-code) folder.\n\n## Contribute\n\nAny Contributing is welcome, You could help with writing test. More example, or any new ideas to the package\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschabanbo%2Fqlevar_router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschabanbo%2Fqlevar_router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschabanbo%2Fqlevar_router/lists"}