溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Flutter自動路由插件auto_route如何使用

發(fā)布時間:2022-08-12 11:15:12 來源:億速云 閱讀:260 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下Flutter自動路由插件auto_route如何使用的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    一、簡介

    在Flutter應(yīng)用開發(fā)過程中,多個頁面的跳轉(zhuǎn)需要使用路由,除了官方提供的Navigator外,我們還可以使用一些第三方路由框架來實現(xiàn)頁面的管理和導航,如Fluro、Frouter等。不過,今天要給大家介紹的是另一款路由框架auto_route。

    auto_route是一個設(shè)計精簡、低耦合的路由框架,支持自動生成路由代碼、動態(tài)添加路由、以及路由的參數(shù)傳遞等功能。相比其他的路由框架,auto_route的使用也更加簡潔。

    二、基本使用

    2.1 安裝插件

    和其他Flutter插件的使用流程一樣,使用之前需要先在項目中安裝auto_route插件,安裝的的腳本如下:

    dependencies:              
      auto_route: [latest-version]              
    dev_dependencies:              
      auto_route_generator: [latest-version]              
      build_runner:

    2.2 定義路由表

    接下來,定義一個路由表的管理類,用來同意管理應(yīng)用的路由,需要使用@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

    等待命令執(zhí)行完成之后,即可在app_router.dart同級的目錄下生成一個app_route.gr.dart文件,也是我們執(zhí)行路由跳轉(zhuǎn)時需要用到的代碼。最后,我們打開main.dart入口文件,然后注冊路由文件。

    class App extends StatelessWidget {      
      final _appRouter = AppRouter();      
      @override      
      Widget build(BuildContext context){      
        return MaterialApp.router(      
          routerDelegate: _appRouter.delegate(),      
          routeInformationParser: _appRouter.defaultRouteParser(),      
        );      
      }      
    }

    2.3 生成路由

    當然,auto_route還支持為每個聲明的 AutoRoute 生成一個 PageRouteInfo 對象,這些對象包含路徑信息以及從頁面的默認構(gòu)造函數(shù)中提取的強類型頁面參數(shù)。

    class BookListRoute extends PageRouteInfo {              
      const BookListRoute() : super(name, path: '/books');              
      static const String name = 'BookListRoute';              
    }

    并且,如果聲明的路由有子路由,那么 AutoRoute 會在其構(gòu)造函數(shù)中添加一個子參數(shù),如下。

    class UserRoute extends PageRouteInfo {              
       UserRoute({List<PagerouteInfo> children}) :              
        super(              
             name,               
             path: '/user/:id',              
             initialChildren: children);              
      static const String name = 'UserRoute';              
    }

    2.4 路由跳轉(zhuǎn)

    和其他的路由框架一樣,AutoRouter 也提供常見的 push、pop 和 remove 方法。比如,我們要打一個新的頁面,那么可以使用下面

    AutoRouter.of(context).replaceAll([const LoginRoute()]);  //LoginRoute為路由
    //或者
    AutoRouter.of(context).navigate(const BooksListRoute())

    如果我們使用的是命名路由,那么可以使用navigateNamed()方法,如下。

    AutoRouter.of(context).pushNamed('/books') ;

    當然,很多時候,路由的跳轉(zhuǎn)還會涉及很多的參數(shù)傳遞,那么對于需要傳遞參數(shù)的路由,我們需要怎么處理呢?對于參數(shù)傳遞,我們可以在目標路由頁面使用構(gòu)造函數(shù)的方式,然后再用AutoRouter進行傳遞。

    AutoRouter.of(context).pushAll([IndexRoute(login: true)]);

    除了跳轉(zhuǎn),我們還可能需要處理路由彈棧的場景,對于彈棧,需要用到pop()函數(shù)。和其他的路由框架一樣,pop()默認只彈出一個,如果要彈出多個,可以使用下面的方式。

    //彈出到指定的路由        
    context.router.popUntilRouteWithName('HomeRoute');          
    //彈出到最頂部
    context.router.popUntilRoot();

     如果要清除,或者刪除路由棧里面的內(nèi)容,可以是呀AutoRouter還提供了remove()函數(shù)。

    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();

    2.5 處理返回結(jié)果

    有時候,兩個路由之間,需要獲取頁面的處理結(jié)果,并將結(jié)果返回給上一個頁面。對于這種場景,只需要在返回的時候返回結(jié)果即可,并在上一個路由使用await進行接收。

    router.pop<bool>(true);    
    var result = await router.push<bool>(LoginRoute());

    三、路由導航

    3.1 嵌套導航

    在應(yīng)用開發(fā)中,嵌套導航是一種比較常見的場景,這意味著,在一個路由頁面中嵌套另外的多個路由。

    Flutter自動路由插件auto_route如何使用

    嵌套路由就像父路由的子字段一樣。在上面的示例中,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 {}

    要完成嵌套路由渲染和構(gòu)建,我們需要在嵌套路由的最外層使用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(),          
            )          
          ],          
        );          
      }          
    }

    如果我們需要跳轉(zhuǎn)到嵌套路由的子組件,我們使用下面的方式就可以導航到嵌套路由的子路由。

    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),              
          ],              
        ),

    3.2 Tab 導航

    前面我們介紹的都是棧管理,即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',...),          
                  ],          
                ));          
          },          
        );          
      }          
    }

    當然,上面的代碼看起來有點復雜,所以如果我們只是實現(xiàn)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',...),          
                  ],          
                )),                 
           }          
        );          
    }

    3.3 PageView

    當然,我們也可以使用 AutoTabsRouter.pageView 構(gòu)造函數(shù)來實現(xiàn)使用 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',...),          
                        ],          
                      ),    
                ),      
          ); },    
       );

    3.4 聲明式導航

    聲明式導航需要與 auto_route 一起使用,只需要使用 AutoRouter.declarative 構(gòu)造函數(shù)并返回基于狀態(tài)的路由列表即可。

    AutoRouter.declarative(      
      routes: (handler) => [      
         BookListRoute(),      
         if(_selectedBook != null)      
         BookDetailsRoute(id: _selectedBook.id),      
     ],);

    四、高級用法

    4.1 路由控制器

    事實上,每個嵌套的 AutoRouter 都有自己的路由控制器來管理其內(nèi)部的堆棧,獲得路由控制器最簡單的方法是使用上下文。在前面的示例中,我們調(diào)用的 AutoRouter.of(context) 就是用來獲得根路由控制器的。

    需要說明的是,對于渲染嵌套路由的 AutoRouter 小部件,我們使用上面的方式獲取的 是小部件樹中最近的父控制器而不是根控制器,下面是一個典型的路由控制器的結(jié)構(gòu)示意圖。

    Flutter自動路由插件auto_route如何使用

    從上圖中可以看出,我們可以通過調(diào)用 router.parent() 來訪問父路由控制器,對于這個通用函數(shù),在真正調(diào)用的時候,我們還需要指定類型,比如StackRouter/TabsRouter。

    router.parent<StackRouter>()  
    router.parent<TabsRouter>()

    當然,如果是獲取根路由控制器,那么是不需要進行類型轉(zhuǎn)換的,因為它始終是 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的情況下,使用下面的方式獲取路由控制器,條件是這個路由已經(jīng)啟動,這個有點類似于Java的反射機制。            

    context.innerRouterOf<StackRouter>(UserRoute.name)                        
    context.innerRouterOf<TabsRouter>(UserRoute.name)

    4.2 Paths

    在 AutoRoute 中,使用路徑是可選的,因為 PageRouteInfo 對象是按名稱匹配的,除非使用根委托中的 initialDeepLink、pushNamed、replaceNamed和navigateNamed 等方法。

    如果我們不指定路徑,系統(tǒng)將自動生成路徑,例如BookListPage 將“book-list-page”作為路徑,如果初始 arg 設(shè)置為 true,則路徑將為“/”。在Flutter開發(fā)中,當頁面層級比較深時,就可以使用paths方式。

    AutoRoute(path: '/books', page: BookListPage),
    4.2.1 Path Parameters

    當然,我們還可以在paths中添加參數(shù)。

    AutoRoute(path: '/books/:id', page: BookDetailsPage),

    然后,我們只需要在目標路由使用 @PathParam('optional-alias') 方式即可獲取傳遞的參數(shù),比如。

    class BookDetailsPage extends StatelessWidget {              
      const BookDetailsPage({@PathParam('id') this.bookId});          
      final int bookId;              
      ...
    4.2.2 Inherited Path Parameters

    不過,如果使用 @PathParm() 標識的構(gòu)造函數(shù)參數(shù)與路由沒有同名的路徑參數(shù)但它的父級有,那么該路徑參數(shù)將被繼承并且生成的路由不會將此作為參數(shù)。

    AutoRoute(  
    	  path: '/product/:id',  
    	  page: ProductScreen,  
    	  children: [  
    		  AutoRoute(path: 'review',page: ProductReviewScreen),  
    	 ],
     ),

    當然,我們還可以在路由頁面添加一個名為 id 的路徑參數(shù),從上面的示例中,我們知道ProductReviewScreen沒有路徑參數(shù),在這種情況下,auto_route 將檢查是否有任何祖先路徑可以提供此路徑參數(shù),如果有則會標記它作為路徑參數(shù),否則會引發(fā)錯誤。

    class ProductReviewScreen extends StatelessWidget {  
      const ProductReviewScreen({super.key, @pathParam required String id}); 
    }
    4.2.3 Query Parameters 

    和前面的查詢參數(shù)的方式相同,只需使用 @QueryParam('optional-alias') 注解構(gòu)造函數(shù)參數(shù)即可獲取參數(shù)的值。

    RouteData.of(context).pathParams;                          
    context.routeData.queryParams

    如果參數(shù)名稱與路徑/查詢參數(shù)相同,則可以使用 const @pathParam 或者@queryParam 并且不需要傳遞 slug/別名,比如。

    class BookDetailsPage extends StatelessWidget {              
      const BookDetailsPage({@pathParam this.id});          
      final int id;              
      ...
    4.2.4 Redirecting Paths

    當然,我們也可以使用RedirectRoute來實現(xiàn)路徑的重定向,重定向路徑時需要使用redirectTo參數(shù)指定重定后的路由,比如。

    <AutoRoute> [              
         RedirectRoute(path: '/', redirectTo: '/books'),              
         AutoRoute(path: '/books', page: BookListPage),              
     ]

    當然,使用重定向時還可以跟一些參數(shù),比如。

    <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: '/')

    4.3 路由守護

    我們可以將路由守衛(wèi)視為中間件或者攔截器,不經(jīng)過分配的守衛(wèi)無法將路由添加到堆棧中,這對于限制對某些路由的訪問是很有用,相當于在執(zhí)行路由跳轉(zhuǎn)前我們可以對路由做一些限制。

    下面,我們使用 AutoRouteGuard 創(chuàng)建一個路由保護,然后在 onNavigation 方法中實現(xiàn)我們的路由邏輯。

    class AuthGuard extends AutoRouteGuard {          
     @override          
     void onNavigation(NavigationResolver resolver, StackRouter router) {          
          //觸發(fā)條件          
         if(authenitcated){                 
            resolver.next(true);          
          }else{                 
             router.push(LoginRoute(onResult: (success){              
                   resolver.next(success);          
              }));          
             }              
         }          
    }

    在onNavigation方法中,NavigationResolver 對象包含可以調(diào)用的屬性,所以我們可以使用resolver.route 訪問的受保護路由,以及調(diào)用resolver.pendingRoutes 訪問掛起的路由列表。

    接下來,我們將守衛(wèi)分配給我們想要保護的路線即可,使用方式如下。

    AutoRoute(page: ProfileScreen, guards: [AuthGuard]);

    有時候,我們希望獲取父窗口包裹的小部件的上下文提供的一些值,那么只需實現(xiàn) AutoRouteWrapper,并讓 WrapRoute(context) 方法返回小部件的子級即可。

    class ProductsScreen extends StatelessWidget implements AutoRouteWrapper {          
      @override          
      Widget wrappedRoute(BuildContext context) {          
      return Provider(create: (ctx) => ProductsBloc(), child: this);      
      }          
      ...

    4.4 路由觀察者

    為了方便查看路由棧的具體情況,我們可以通過擴展 AutoRouterObserver 來實現(xiàn),然后重寫里面的函數(shù)來進行查看,比如。

    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如何使用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI