Flutter中g(shù)o_router路由管理的使用指南
前言
go_router
是一個 Flutter 的第三方路由插件,相比 Flutter 自帶的路由,go_router
更加靈活,而且簡單易用。在 App 應用中,如果你想自己控制路由的定義和管理方式,那么它將十分有用。同時,對于 Web 應用來說,go_router
也提供了很好的支持。 使用 go_router
后,你可以定義 URL 的格式,使用 URL 跳轉(zhuǎn),處理深度鏈接以及其他一系列的導航相關的應用場景。
GoRouter 特性
GoRouter
針對頁面導航提供了下面這些特性:
- 使用模板語法解析路由路徑和路由查詢(
query
)參數(shù); - 支持單個目標路由展示多個頁面(子路由);
- 重定向:可以基于應用狀態(tài)跳轉(zhuǎn)到不同的URL,比如用戶沒有登錄時跳轉(zhuǎn)到登錄頁;
- 使用
StatefulShellRoute
可以支持嵌套的 Tab 導航; - 同時支持
Material
風格和Cupertino
風格應用; - 兼容
Navigator
API 。
添加插件
當前最新版本的 go_router
是10.0.0(6.3.0版本以上需要 Dart 2.18),可以根據(jù)自己的需要添加相應的版本。在 pubspec.yaml 中加入依賴的版本即可,下面是以7.1.1版本為例。
dependencies: go_router: ^7.1.1
路由配置
引入 go_router
插件后,就可以在應用中配置 GoRouter
,代碼如下:
import 'package:go_router/go_router.dart'; // GoRouter configuration final _router = GoRouter( initialLocation: '/', routes: [ GoRoute( name: 'home', // Optional, add name to your routes. Allows you navigate by name instead of path path: '/', builder: (context, state) => HomeScreen(), ), GoRoute( name: 'page2', path: '/page2', builder: (context, state) => Page2Screen(), ), ], );
然后,我們就可以通過MaterialApp.router
或CupertinoApp.router
構(gòu)造函數(shù)來使用 GoRouter
,并且將 routerConfig
參數(shù)設置為我們前面定義的 GoRouter
配置對象。
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, ); } }
接下來就可以愉快地玩耍 GoRouter
了。
路由參數(shù)
GoRouter
的每一個路由都通過 GoRoute
對象來配置,我們可以在構(gòu)建 GoRoute
對象時來配置路由參數(shù)。路由參數(shù)典型的就是路徑參數(shù),比如 /path/:{路徑參數(shù)}
,這個時候 GoRoute
的路徑參數(shù)和很多 Web 框架的路由是一樣的,通過一個英文冒號加參數(shù)名稱就可以配置,之后我們可以在回調(diào)方法中通過 GoRouterState
對象獲取路徑參數(shù),這個參數(shù)就可以傳遞到路由跳轉(zhuǎn)目的頁面。
GoRoute( path: '/fruits/:id', builder: (context, state) { final id = state.params['id'] // Get "id" param from URL return FruitsPage(id: id); }, ),
同樣,也可以從GoRouterState
中獲取 URL 路徑中的查詢(query)參數(shù),例如下面的代碼就是從/fruits?search=antonio
中獲取search
參數(shù)。
GoRoute( path: '/fruits', builder: (context, state) { final search = state.queryParams['search']; return FruitsPage(search: search); }, ),
添加子路由
路由匹配后可以支持多個頁面(即子路由),當一個新的頁面在舊的頁面之上展示時,這個時候的效果和調(diào)用 push
方法是一樣的,。如果頁面提供了 AppBar 組件的話,那么會自動增加返回按鈕。 要使用子路由,我們只需要在上級路由中增加對應的下級路由即可,代碼如下。
GoRoute( path: '/fruits', builder: (context, state) { return FruitsPage(); }, routes: <RouteBase>[ // Add child routes GoRoute( path: 'fruits-details', // NOTE: Don't need to specify "/" character for router's parents builder: (context, state) { return FruitDetailsPage(); }, ), ], )
頁面導航
GoRouter 提供了多種方式跳轉(zhuǎn)到目的頁面,比如使用context.go()
跳轉(zhuǎn)到指定的 URL 地址。
build(BuildContext context) { return TextButton( onPressed: () => context.go('/fruits/fruit-detail'), ); }
也可以使用路由的名稱進行跳轉(zhuǎn),這個時候調(diào)用context.goNamed()
即可。
build(BuildContext context) { return TextButton( // remember to add "name" to your routes onPressed: () => context.goNamed('fruit-detail'), ); }
如果要構(gòu)建查詢參數(shù),那么可以使用 Uri 類來構(gòu)建路由路徑。
context.go( Uri( path: '/fruit-detail', queryParameters: {'id': '10'}, ).toString(), );
如果要從當前頁面返回的話,調(diào)用context.pop()
即可。
嵌套導航
有些應用在同一個頁面展示多個子頁面,例如 BottomNavigationBar
在進行導航的時候就可以一直保留在屏幕底部。這種其實是嵌套導航,在 GoRouter
里,可以通過StatefulShellRoute
來實現(xiàn)。 StatefulShellRoute
不直接使用根導航(root Navigator),而是通過不同的導航的子路由來實現(xiàn)嵌套導航。對于每一個導航分支,都會創(chuàng)建各自獨立的導航,相當于是一個并行導航樹,從而實現(xiàn)了有狀態(tài)的嵌套導航。 當我們使用BottomNavigationBar
的時候,就可以為很方便地為每一個 Tab 配置一個持久的導航狀態(tài)。 StatefulShellRoute
通過指定一個StatefulShellBranch
類型的列表來完成,列表每一個元素代表路由樹的一個獨立的有狀態(tài)的分支。StatefulShellBranch
為每個分支提供了根路由和 Navigator
key(GlobalKey
),并提供了可選的初始默認路由地址。 我們來看看具體怎么實現(xiàn)。 首先創(chuàng)建我們的 GoRouter
對象,這個時候我們需要添加StatefulShellRoute.indexedStack
到路由中,這個類負責創(chuàng)建嵌套路由。StatefulShellRoute.indexedStack() 實際上是使用了 IndexedStack
創(chuàng)建了一個StatefulShellRoute
。 這個構(gòu)造函數(shù)使用IndexedStack
來管理每個分支導航的頁面,示例代碼如下:
// Create keys for `root` & `section` navigator avoiding unnecessary rebuilds final _rootNavigatorKey = GlobalKey<NavigatorState>(); final _sectionNavigatorKey = GlobalKey<NavigatorState>(); final router = GoRouter( navigatorKey: _rootNavigatorKey, initialLocation: '/feed', routes: <RouteBase>[ StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { // Return the widget that implements the custom shell (e.g a BottomNavigationBar). // The [StatefulNavigationShell] is passed to be able to navigate to other branches in a stateful way. return ScaffoldWithNavbar(navigationShell); }, branches: [ // The route branch for the 1o Tab StatefulShellBranch( navigatorKey: _sectionNavigatorKey, // Add this branch routes // each routes with its sub routes if available e.g feed/uuid/details routes: <RouteBase>[ GoRoute( path: '/feed', builder: (context, state) => const FeedPage(), routes: <RouteBase>[ GoRoute( path: 'detail', builder: (context, state) => const FeedDetailsPage(), ) ], ), ], ), // The route branch for 2o Tab StatefulShellBranch(routes: <RouteBase>[ // Add this branch routes // each routes with its sub routes if available e.g shope/uuid/details GoRoute( path: '/shope', builder: (context, state) => const ShopePage(), ), ]) ], ), ], );
在上面的代碼中,我們在路由中加入了StatefulShellRoute.indexedStack()
,由它負責創(chuàng)建路由分支以及返回一個自定義的導航殼,這里是BottomNavigationBar
。
- 在 builder 參數(shù)中, 我們返回導航用的殼,一個簡單的帶有
BottomNavigationBar
的Scaffold
,這里需要記得將navigationShell
傳給頁面,因為我們需要用它來導航到其他分支,例如從Home到 Shope。 - 在路由分支數(shù)組
branches
中,我們提供了一個StatefulShellBranch
的數(shù)組。這里只需要給第一個元素的navigatorKey
提供之前創(chuàng)建的全局的_sectionNavigatorKey
。其他分支則使用默認的 key。同時,為每個分支提供了一個RouteBase
列表,該列表是對應分支的路由。
下面是我們定義的帶有BottomNavigationBar
的自定義導航殼的代碼。
import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class ScaffoldWithNavbar extends StatelessWidget { const ScaffoldWithNavbar(this.navigationShell, {super.key}); /// The navigation shell and container for the branch Navigators. final StatefulNavigationShell navigationShell; @override Widget build(BuildContext context) { return Scaffold( body: navigationShell, bottomNavigationBar: BottomNavigationBar( currentIndex: navigationShell.currentIndex, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), BottomNavigationBarItem(icon: Icon(Icons.shop), label: 'Shope'), ], onTap: _onTap, ), ); } void _onTap(index) { navigationShell.goBranch( index, // A common pattern when using bottom navigation bars is to support // navigating to the initial location when tapping the item that is // already active. This example demonstrates how to support this behavior, // using the initialLocation parameter of goBranch. initialLocation: index == navigationShell.currentIndex, ); } }
在上面的代碼中,實際上就是構(gòu)建帶有BottomNavigationBar
的Scaffold
然后 body 是從路由里獲取的navigationShell
. 路由分支及頁面的切換通過_onTap(index)
實現(xiàn),當點擊某個 Tab 時,就使用navigationShell.goBranch(index)
來完成切換動作。完整代碼:flutter-go_router-with-nested-tab-navigation。
路由守衛(wèi)(Guards)
有些路由地址需要守衛(wèi),例如對于沒有登錄的用戶,有些頁面就無法訪問。GoRouter
可以設置全局的重定向路由。最常見的一個場景就是對于沒有登錄的用戶,跳轉(zhuǎn)到/login
登錄頁面。 在 GoRouter 中,可以通過redirect
參數(shù)配置重定向,這是一個GoRouterRedirect
的回調(diào)方法。如果要基于應用狀態(tài)更改跳轉(zhuǎn)都只,那么既可以在GoRouter
或GoRoute
的構(gòu)造方法中增加redirect
參數(shù)。其中 GoRoute
是只針對當前路由進行跳轉(zhuǎn)處理,而GoRouter
這是全局處理。下面是示例代碼,如果不需要跳轉(zhuǎn),則在回調(diào)方法中返回 null
即可。
GoRouter( redirect: (BuildContext context, GoRouterState state) { final isAuthenticated = // your logic to check if user is authenticated if (!isAuthenticated) { return '/login'; } else { return null; // return "null" to display the intended route without redirecting } }, ...
也可以指定一個redirectLimit
參數(shù)來限制最大的跳轉(zhuǎn)次數(shù),這個值默認是5。如果超過了跳轉(zhuǎn)次數(shù),則會顯示一個錯誤頁面。
轉(zhuǎn)場動畫
GoRouter
支持為每個 GoRoute
自定義轉(zhuǎn)場動畫,這可以通過GoRoute
的構(gòu)造函數(shù)的pageBuilder
參數(shù)來完成,下面是示例代碼。
GoRoute( path: '/fruit-details', pageBuilder: (context, state) { return CustomTransitionPage( key: state.pageKey, child: FruitDetailsScreen(), transitionsBuilder: (context, animation, secondaryAnimation, child) { // Change the opacity of the screen using a Curve based on the the animation's value return FadeTransition( opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation), child: child, ); }, ); }, ),
完整的示例代碼可以在 GitHub 查看:自定義轉(zhuǎn)場動畫源碼。
錯誤處理(404頁面)
go_router
為MaterialApp
和CupertinoApp
定義了默認的錯誤頁面,也可以通過 errorBuilder 參數(shù)自定義錯誤頁面,代碼如下。
GoRouter( /* ... */ errorBuilder: (context, state) => ErrorPage(state.error), );
類型安全路由
除了使用 URL 進行路由導航外,go_router 也通過go_router_builder
插件提供了類型安全路由,這可以通過代碼生成來完成。要使用這種方式,需要在pubspec.yaml
增加下面這些依賴。
dev_dependencies: go_router_builder: ^1.0.16 build_runner: ^2.3.3 build_verify: ^3.1.0
定義路由
Then define each route as a class extending GoRouteData and overriding the build method.
class HomeRoute extends GoRouteData { const HomeRoute(); @override Widget build(BuildContext context, GoRouterState state) => const HomeScreen(); }
路由樹
路由樹基于每個頂層的路由來定義,代碼如下。
import 'package:go_router/go_router.dart'; part 'go_router.g.dart'; // name of generated file // Define how your route tree (path and sub-routes) @TypedGoRoute<HomeScreenRoute>( path: '/home', routes: [ // Add sub-routes TypedGoRoute<SongRoute>( path: 'song/:id', ) ] ) // Create your route screen that extends "GoRouteData" and @override "build" // method that return the screen for this route @immutable class HomeScreenRoute extends GoRouteData { @override Widget build(BuildContext context) { return const HomeScreen(); } } @immutable class SongRoute extends GoRouteData { final int id; const SongRoute({required this.id}); @override Widget build(BuildContext context) { return SongScreen(songId: id.toString()); } }
之后可以運行代碼生成來構(gòu)建類型安全路由。
flutter pub global activate build_runner // Optional, if you already have build_runner activated so you can skip this step flutter pub run build_runner build
導航的時候,就不再需要使用 URL的方式了,可以構(gòu)建一個GoRouteData對象然后調(diào)用go()
即可。
TextButton( onPressed: () { const SongRoute(id: 2).go(context); }, child: const Text('Go to song 2'), ),
路由跳轉(zhuǎn)監(jiān)測
go_router還提供了一個非常有用的特性,那就是路由導航監(jiān)測NavigatorObserver
??梢酝ㄟ^給GoRouter增加一個NavigatorObserver
對象來監(jiān)聽路由行為,例如 push
、pop
或路由替換(replace
)。這可以通過自定義 NavigatorObserver
的子類完成。
class MyNavigatorObserver extends NavigatorObserver { @override void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { log('did push route'); } @override void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { log('did pop route'); } }
之后,在GoRouter
的 observers
參數(shù)中增加自定義的MyNavigatorObserver
即可完成對所有觸發(fā)路由跳轉(zhuǎn)的行為的監(jiān)聽。
GoRouter( ... observers: [ // Add your navigator observers MyNavigatorObserver(), ], ... )
完整的示例代碼見 GitHub: flutter-with-go_router: A simple app to show how to work with go_router。
到此這篇關于Flutter中g(shù)o_router路由管理的使用指南的文章就介紹到這了,更多相關Flutter go_router內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android水波紋載入控件CircleWaterWaveView使用詳解
這篇文章主要為大家詳細介紹了Android水波紋載入控件CircleWaterWaveView使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01Android onMeasure與onDraw及自定義屬性使用示例
這篇文章主要介紹了Android onMeasure與onDraw及自定義屬性使用示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2023-02-02Android布局ConstraintLayout代碼修改約束及輔助功能
這篇文章主要為大家介紹了Android布局ConstraintLayout代碼修改約束及輔助功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09