您好,登錄后才能下訂單哦!
今天小編給大家分享一下Flutter自動路由插件auto_route如何使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
在Flutter應用開發過程中,多個頁面的跳轉需要使用路由,除了官方提供的Navigator外,我們還可以使用一些第三方路由框架來實現頁面的管理和導航,如Fluro、Frouter等。不過,今天要給大家介紹的是另一款路由框架auto_route。
auto_route是一個設計精簡、低耦合的路由框架,支持自動生成路由代碼、動態添加路由、以及路由的參數傳遞等功能。相比其他的路由框架,auto_route的使用也更加簡潔。
和其他Flutter插件的使用流程一樣,使用之前需要先在項目中安裝auto_route插件,安裝的的腳本如下:
dependencies: auto_route: [latest-version] dev_dependencies: auto_route_generator: [latest-version] build_runner:
接下來,定義一個路由表的管理類,用來同意管理應用的路由,需要使用@MaterialAutoRouter注解進行標識,如下。
@MaterialAutoRouter( replaceInRouteName: 'Page,Route', routes: <AutoRoute>[ AutoRoute(page: BookListPage, initial: true), AutoRoute(page: BookDetailsPage), ], ) class $AppRouter {}
要生成路由文件的一部分而不是獨立的 AppRouter 類,只需將 Part 指令添加到AppRouter 并擴展生成的私有路由器即可。
part 'app_router.gr.dart'; @MaterialAutoRouter( replaceInRouteName: 'Page,Route', routes: <AutoRoute>[ AutoRoute(page: BookListPage, initial: true), AutoRoute(page: BookDetailsPage), ], ) class AppRouter extends _$AppRouter{}
接下來,我們使用build_runner提供的命令即可生成路由代碼。
//自動刷新路由表 flutter packages pub run build_runner watch //生成路由代碼 flutter packages pub run build_runner build
等待命令執行完成之后,即可在app_router.dart同級的目錄下生成一個app_route.gr.dart文件,也是我們執行路由跳轉時需要用到的代碼。最后,我們打開main.dart入口文件,然后注冊路由文件。
class App extends StatelessWidget { final _appRouter = AppRouter(); @override Widget build(BuildContext context){ return MaterialApp.router( routerDelegate: _appRouter.delegate(), routeInformationParser: _appRouter.defaultRouteParser(), ); } }
當然,auto_route還支持為每個聲明的 AutoRoute 生成一個 PageRouteInfo 對象,這些對象包含路徑信息以及從頁面的默認構造函數中提取的強類型頁面參數。
class BookListRoute extends PageRouteInfo { const BookListRoute() : super(name, path: '/books'); static const String name = 'BookListRoute'; }
并且,如果聲明的路由有子路由,那么 AutoRoute 會在其構造函數中添加一個子參數,如下。
class UserRoute extends PageRouteInfo { UserRoute({List<PagerouteInfo> children}) : super( name, path: '/user/:id', initialChildren: children); static const String name = 'UserRoute'; }
和其他的路由框架一樣,AutoRouter 也提供常見的 push、pop 和 remove 方法。比如,我們要打一個新的頁面,那么可以使用下面
AutoRouter.of(context).replaceAll([const LoginRoute()]); //LoginRoute為路由 //或者 AutoRouter.of(context).navigate(const BooksListRoute())
如果我們使用的是命名路由,那么可以使用navigateNamed()方法,如下。
AutoRouter.of(context).pushNamed('/books') ;
當然,很多時候,路由的跳轉還會涉及很多的參數傳遞,那么對于需要傳遞參數的路由,我們需要怎么處理呢?對于參數傳遞,我們可以在目標路由頁面使用構造函數的方式,然后再用AutoRouter進行傳遞。
AutoRouter.of(context).pushAll([IndexRoute(login: true)]);
除了跳轉,我們還可能需要處理路由彈棧的場景,對于彈棧,需要用到pop()函數。和其他的路由框架一樣,pop()默認只彈出一個,如果要彈出多個,可以使用下面的方式。
//彈出到指定的路由 context.router.popUntilRouteWithName('HomeRoute'); //彈出到最頂部 context.router.popUntilRoot();
如果要清除,或者刪除路由棧里面的內容,可以是呀AutoRouter還提供了remove()函數。
context.router.removeLast(); context.router.removeWhere((route) => );
下面是AutoRouter常用方法的一個匯總。
context.pushRoute(const BooksListRoute()); context.replaceRoute(const BooksListRoute()); context.navigateTo(const BooksListRoute()); context.navigateNamedTo('/books'); context.navigateBack(); context.popRoute();
有時候,兩個路由之間,需要獲取頁面的處理結果,并將結果返回給上一個頁面。對于這種場景,只需要在返回的時候返回結果即可,并在上一個路由使用await進行接收。
router.pop<bool>(true); var result = await router.push<bool>(LoginRoute());
在應用開發中,嵌套導航是一種比較常見的場景,這意味著,在一個路由頁面中嵌套另外的多個路由。
嵌套路由就像父路由的子字段一樣。在上面的示例中,UsersPage、PostsPage 和SettingsPage就是DashboardPage的子路由,所以它們的定義如下。
@MaterialAutoRouter( replaceInRouteName: 'Page,Route', routes: <AutoRoute>[ AutoRoute( path: '/dashboard', page: DashboardPage, children: [ AutoRoute(path: 'users', page: UsersPage), AutoRoute(path: 'posts', page: PostsPage), AutoRoute(path: 'settings', page: SettingsPage), ], ), AutoRoute(path: '/login', page: LoginPage) ], ) class $AppRouter {}
要完成嵌套路由渲染和構建,我們需要在嵌套路由的最外層使用AutoRouter 的小部件,如下。
class DashboardPage extends StatelessWidget { @override Widget build(BuildContext context) { return Row( children: [ Column( children: [ NavLink(label: 'Users', destination: const UsersRoute()), NavLink(label: 'Posts', destination: const PostsRoute()), NavLink(label: 'Settings', destination: const SettingsRoute()), ], ), Expanded( // nested routes will be rendered here child: AutoRouter(), ) ], ); } }
如果我們需要跳轉到嵌套路由的子組件,我們使用下面的方式就可以導航到嵌套路由的子路由。
AutoRoute( path: '/dashboard', page: DashboardPage, children: [ AutoRoute(path: '', page: UsersPage), //The same thing can be done using the initial flag //AutoRoute(page: UsersPage,initial: true), AutoRoute(path: 'posts', page: PostsPage), ], ),
前面我們介紹的都是棧管理,即StackRouter,遵循先進后出的邏輯。除了支持StackRouter,auto_route還支持Tab Navigation,下面是示例代碼。
class DashboardPage extends StatelessWidget { @override Widget build(BuildContext context) { return AutoTabsRouter( routes: const [ UsersRoute(), PostsRoute(), SettingsRoute(), ], builder: (context, child, animation) { final tabsRouter = AutoTabsRouter.of(context); return Scaffold( body: FadeTransition( opacity: animation, child: child, ), bottomNavigationBar: BottomNavigationBar( currentIndex: tabsRouter.activeIndex, onTap: (index) { tabsRouter.setActiveIndex(index); }, items: [ BottomNavigationBarItem(label: 'Users',...), BottomNavigationBarItem(label: 'Posts',...), BottomNavigationBarItem(label: 'Settings',...), ], )); }, ); } }
當然,上面的代碼看起來有點復雜,所以如果我們只是實現Tab導航,那么可以使用下面的簡潔代碼。
class DashboardPage extends StatelessWidget { @override Widget build(context) { @override Widget build(context) { return AutoTabsScaffold( routes: const [ UsersRoute(), PostsRoute(), SettingsRoute(), ], bottomNavigationBuilder: (_,tabsRouter) { return BottomNavigationBar( currentIndex: tabsRouter.activeIndex, onTap: tabsRouter.setActiveIndex items: [ BottomNavigationBarItem(label: 'Users',...), BottomNavigationBarItem(label: 'Posts',...), BottomNavigationBarItem(label: 'Settings',...), ], )), } ); }
當然,我們也可以使用 AutoTabsRouter.pageView 構造函數來實現使用 PageView 的選項卡。
AutoTabsRouter.pageView( routes: [ BooksTab(), ProfileTab(), SettingsTab(), ], builder: (context, child, _) { return Scaffold( appBar: AppBar( title: Text(context.topRoute.name), leading: AutoLeadingButton()), body: child, bottomNavigationBar: BottomNavigationBar( currentIndex: tabsRouter.activeIndex, onTap: tabsRouter.setActiveIndex items: [ BottomNavigationBarItem(label: 'Books',...), BottomNavigationBarItem(label: 'Profile',...), BottomNavigationBarItem(label: 'Settings',...), ], ), ), ); }, );
聲明式導航需要與 auto_route 一起使用,只需要使用 AutoRouter.declarative 構造函數并返回基于狀態的路由列表即可。
AutoRouter.declarative( routes: (handler) => [ BookListRoute(), if(_selectedBook != null) BookDetailsRoute(id: _selectedBook.id), ],);
事實上,每個嵌套的 AutoRouter 都有自己的路由控制器來管理其內部的堆棧,獲得路由控制器最簡單的方法是使用上下文。在前面的示例中,我們調用的 AutoRouter.of(context) 就是用來獲得根路由控制器的。
需要說明的是,對于渲染嵌套路由的 AutoRouter 小部件,我們使用上面的方式獲取的 是小部件樹中最近的父控制器而不是根控制器,下面是一個典型的路由控制器的結構示意圖。
從上圖中可以看出,我們可以通過調用 router.parent() 來訪問父路由控制器,對于這個通用函數,在真正調用的時候,我們還需要指定類型,比如StackRouter/TabsRouter。
router.parent<StackRouter>() router.parent<TabsRouter>()
當然,如果是獲取根路由控制器,那么是不需要進行類型轉換的,因為它始終是 StackRouter。
router.root
另一方面,為了在其他地方使用這個路由控制器,可以定義一個全局的key,比如。
class DashboardPage extends StatefulWidget { @override _DashboardPageState createState() => _DashboardPageState(); } class _DashboardPageState extends State<DashboardPage> { final _innerRouterKey = GlobalKey<AutoRouterState>(); @override Widget build(BuildContext context) { return Row( children: [ Column( children: [ NavLink(label: 'Users', onTap:(){ final router = _innerRouterKey.currentState?.controller; router?.push(const UsersRoute()); } ), ... ], ), Expanded( child: AutoRouter(key: _innerRouterKey), ) ], ); } }
當然,我們也可以在沒有全局key的情況下,使用下面的方式獲取路由控制器,條件是這個路由已經啟動,這個有點類似于Java的反射機制。
context.innerRouterOf<StackRouter>(UserRoute.name) context.innerRouterOf<TabsRouter>(UserRoute.name)
在 AutoRoute 中,使用路徑是可選的,因為 PageRouteInfo 對象是按名稱匹配的,除非使用根委托中的 initialDeepLink、pushNamed、replaceNamed和navigateNamed 等方法。
如果我們不指定路徑,系統將自動生成路徑,例如BookListPage 將“book-list-page”作為路徑,如果初始 arg 設置為 true,則路徑將為“/”。在Flutter開發中,當頁面層級比較深時,就可以使用paths方式。
AutoRoute(path: '/books', page: BookListPage),
當然,我們還可以在paths中添加參數。
AutoRoute(path: '/books/:id', page: BookDetailsPage),
然后,我們只需要在目標路由使用 @PathParam('optional-alias') 方式即可獲取傳遞的參數,比如。
class BookDetailsPage extends StatelessWidget { const BookDetailsPage({@PathParam('id') this.bookId}); final int bookId; ...
不過,如果使用 @PathParm() 標識的構造函數參數與路由沒有同名的路徑參數但它的父級有,那么該路徑參數將被繼承并且生成的路由不會將此作為參數。
AutoRoute( path: '/product/:id', page: ProductScreen, children: [ AutoRoute(path: 'review',page: ProductReviewScreen), ], ),
當然,我們還可以在路由頁面添加一個名為 id 的路徑參數,從上面的示例中,我們知道ProductReviewScreen沒有路徑參數,在這種情況下,auto_route 將檢查是否有任何祖先路徑可以提供此路徑參數,如果有則會標記它作為路徑參數,否則會引發錯誤。
class ProductReviewScreen extends StatelessWidget { const ProductReviewScreen({super.key, @pathParam required String id}); }
和前面的查詢參數的方式相同,只需使用 @QueryParam('optional-alias') 注解構造函數參數即可獲取參數的值。
RouteData.of(context).pathParams; context.routeData.queryParams
如果參數名稱與路徑/查詢參數相同,則可以使用 const @pathParam 或者@queryParam 并且不需要傳遞 slug/別名,比如。
class BookDetailsPage extends StatelessWidget { const BookDetailsPage({@pathParam this.id}); final int id; ...
當然,我們也可以使用RedirectRoute來實現路徑的重定向,重定向路徑時需要使用redirectTo參數指定重定后的路由,比如。
<AutoRoute> [ RedirectRoute(path: '/', redirectTo: '/books'), AutoRoute(path: '/books', page: BookListPage), ]
當然,使用重定向時還可以跟一些參數,比如。
<AutoRoute> [ RedirectRoute(path: 'books/:id', redirectTo: '/books/:id/details'), AutoRoute(path: '/books/:id/details', page: BookDetailsPage), ]
除此之外,auto_route 還支持使用通配符來匹配無效或未定義的路徑,可以將它作為默認的路徑。
AutoRoute(path: '*', page: UnknownRoutePage) AutoRoute(path: '/profile/*', page: ProfilePage) RedirectRoute(path: '*', redirectTo: '/')
我們可以將路由守衛視為中間件或者攔截器,不經過分配的守衛無法將路由添加到堆棧中,這對于限制對某些路由的訪問是很有用,相當于在執行路由跳轉前我們可以對路由做一些限制。
下面,我們使用 AutoRouteGuard 創建一個路由保護,然后在 onNavigation 方法中實現我們的路由邏輯。
class AuthGuard extends AutoRouteGuard { @override void onNavigation(NavigationResolver resolver, StackRouter router) { //觸發條件 if(authenitcated){ resolver.next(true); }else{ router.push(LoginRoute(onResult: (success){ resolver.next(success); })); } } }
在onNavigation方法中,NavigationResolver 對象包含可以調用的屬性,所以我們可以使用resolver.route 訪問的受保護路由,以及調用resolver.pendingRoutes 訪問掛起的路由列表。
接下來,我們將守衛分配給我們想要保護的路線即可,使用方式如下。
AutoRoute(page: ProfileScreen, guards: [AuthGuard]);
有時候,我們希望獲取父窗口包裹的小部件的上下文提供的一些值,那么只需實現 AutoRouteWrapper,并讓 WrapRoute(context) 方法返回小部件的子級即可。
class ProductsScreen extends StatelessWidget implements AutoRouteWrapper { @override Widget wrappedRoute(BuildContext context) { return Provider(create: (ctx) => ProductsBloc(), child: this); } ...
為了方便查看路由棧的具體情況,我們可以通過擴展 AutoRouterObserver 來實現,然后重寫里面的函數來進行查看,比如。
class MyObserver extends AutoRouterObserver { @override void didPush(Route route, Route? previousRoute) { print('New route pushed: ${route.settings.name}'); } @override void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) { print('Tab route visited: ${route.name}'); } @override void didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) { print('Tab route re-visited: ${route.name}'); } }
然后,我們將觀察者傳遞給根委托 AutoRouterDelegate。
return MaterialApp.router( routerDelegate: AutoRouterDelegate( _appRouter, navigatorObservers: () => [MyObserver()], ), routeInformationParser: _appRouter.defaultRouteParser(), );
以上就是“Flutter自動路由插件auto_route如何使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。