您好,登錄后才能下訂單哦!
這篇文章主要介紹Flutter實現路由與導航的方法,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
如果說構成視圖元素的基本單位是組件,那么構成應用程序的基本單位就是頁面。對于擁有多個頁面的應用程序而言,如何從一個頁面平滑地過渡到另一個頁面,是技術框架需要考慮的問題。
在前端開發中,可以使用路由框架來統一管理頁面及它們之間的跳轉。在Android中路由指的是一個Activity,在iOS中指的是一個ViewController,可以通過startActivity或pushViewController來打開一個新的路由。在Flutter中,路由的管理和導航借鑒了前端和客戶端的設計思路,需要使用Route和Navigator來進行統一管理。
其中,Route是頁面的抽象,主要負責創建界面、接收參數以及響應導航器Navigator的打開與關閉。而Navigator則用于維護路由棧管理,Route打開即入棧,Route關閉即出棧,當然還可以替換棧內的某一個Route。作為官方提供的路由管理組件,Navigator提供了一系列方法來管理路由棧,其中最常用的兩個方法是push()和pop(),它們的含義如下。
除了push()和pop()方法外,Navigator還提供了很多其它實用的方法,如replace()、removeRoute()和popUntil()等,可以根據使用場景合理的選取。
根據是否需要提前注冊頁面標識符,Flutter中的路由管理可以分為基本路由和命名路由兩種。
下面就讓我們重點來看一下Flutter中的路由管理的基本路由和命名路由等相關知識。
基本路由
在Flutter開發中,基本路由的使用方式和原生Android、iOS打開新頁面的方式非常類似。要打開一個新的頁面,只需要創建一個MaterialPageRoute對象實例,然后調用Navigator.push()方法將新頁面壓到路由堆棧的頂部即可,如果要返回上一個頁面,則可以調用Navigator.pop()方法。
其中,MaterialPageRoute是一種路由模板,定義了路由創建以及路由切換過渡動畫的相關配置,該配置可以根據不同的平臺實現與平臺頁面切換動畫風格一致的路由切換動畫。下面是使用Navigator實現頁面跳轉的示例,代碼如下。
class FirstPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('第一個頁面'), ), body: Center( child: RaisedButton( child: Text('跳轉到第二個頁面'), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()))), ), ); } } class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('第二個頁面'), ), body: Center( child: RaisedButton( child: Text('返回上一個頁面'), onPressed: () => Navigator.pop(context)), ), ); } }
在上面的示例中,我們創建了兩個頁面,每個頁面都包含一個按鈕。當點擊第一個頁面上的按鈕時將導航到第二個頁面,點擊第二個頁面上的按鈕將返回第一個頁面。運行上面的代碼,效果如下圖所示。
可以發現,跳轉頁面使用的是Navigator.push()方法,該方法可以將一個新的路由添加到由Navigator管理的路由對象的棧頂。而創建新的路由對象使用的是MaterialPageRoute,MaterialPageRoute是PageRoute的子類,定義了路由創建及切換時過渡動畫的相關接口及屬性,并且自帶頁面切換動畫,Android平臺頁面進入動畫是向上滑動并淡出,退出是相反,iOS平臺頁面進入動畫是從右側滑入,退出則相反。
命名路由
基本路由的使用方式相對簡單靈活,適用于應用中頁面不多的場景。而對于應用中頁面比較多的情況下,如果再使用基本路由方式,那么每次跳轉一個新的頁面都要手動創建MaterialPageRoute實例,然后再調用push()方法來打開一個新的頁面,此時頁面的管理和跳轉就比較混亂。
為了避免頻繁的創建MaterialPageRoute實例,Flutter提供了另外一種方式來簡化路由管理,即命名路由。所謂命名路由,就是給頁面起一個別名,然后使用頁面的別名就可以打開它,使用此種方式來管理路由,使得路由的管理更加清晰直觀。
要想通過別名來指定頁面切換,必須先給應用程序MaterialApp提供一個頁面名稱映射規則,即路由表。路由表是一個Map<String,WidgetBuilder>的結構,其中key對應頁面名字,value則是對應的頁面,如下所示。
MaterialApp( ... //其他配置 routes:{ //注冊路由 'first':(context)=>FirstPage(), 'second':(context)=>SecondPage(), }, initialRoute: 'first', //初始路由頁面 );
在路由表中注冊好頁面后,然后就可以通過Navigator.pushNamed()方法來打開頁面,如下所示。
Navigator.pushNamed(context,"second "); // second表示頁面別名
不過,由于路由的注冊和使用都采用字符串來標識,這就會帶來一個問題,即如果打開一個不存在的路由頁面。對應這類問題,移動應用有一個通用的解決方案,即跳轉到一個統一的錯誤頁面。在注冊路由表時,Flutter提供了一個UnknownRoute屬性,用來對未知的路由標識符進行統一的頁面跳轉處理,如下所示。
MaterialApp( … routes:{}, onUnknownRoute: (RouteSettings setting) => MaterialPageRoute(builder: (context) => UnknownPage()), //錯誤路由處理,返回UnknownPage ); class UnknownPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('錯誤路由'), ), ); } }
路由嵌套
有時候,一個應用可能不止一個導航器,而是可能有多個導航器,將一個導航器嵌套在另一個導航器的行為稱為路由嵌套。路由嵌套在移動開發中是很常見的,比如,移動開發中經常會看到應用主頁有底部導航欄,每個底部導航欄又嵌套其他頁面的情況,效果如下圖所示。
要實現上面的示例效果,首先需要新建一個底部導航欄,然后再由底部導航欄去嵌套其他子路由。關于底部導航欄的實現,可以直接使用Scaffold布局組件的bottomNavigationBar屬性實現,如下所示。
class MainPage extends StatefulWidget { @override State<StatefulWidget> createState() { return MainPageState(); } } class MainPageState extends State<MainPage> { int currentIndex = 0; //底部導航欄索引 final List<Widget> children = [ HomePage(), //首頁 MinePage(), //我的 ]; @override Widget build(BuildContext context) { return Scaffold( body: children[currentIndex], bottomNavigationBar: BottomNavigationBar( onTap: onTabTapped, currentIndex: currentIndex, items: [ BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('首頁')), BottomNavigationBarItem(icon: Icon(Icons.person), title: Text('我的')), ], ), ); } void onTabTapped(int index) { setState(() { currentIndex = index; }); } }
然后,每個底部導航欄會嵌套一個子路由,然后子路由再去管理對應的路由頁面。在Flutter中,創建子路由需要使用Navigator組件,并且子路由的攔截需要使用onGenerateRoute屬性,如下所示。
class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Navigator( initialRoute: 'first', onGenerateRoute: (RouteSettings settings) { WidgetBuilder builder; switch (settings.name) { case 'first': builder = (BuildContext context) => FirstPage(); break; case 'second': builder = (BuildContext context) => SecondPage(); break; } return new MaterialPageRoute(builder: builder, settings: settings); }, ); } }
運行上面的代碼,當點擊子路由頁面上的按鈕時,底部的導航欄欄并不會消失,這是因為子路由僅在自己的范圍內有效。要想跳轉到其他子路由管理的頁面,就需要在根導航器中進行注冊,也就是MaterialApp內部的導航器。
路由傳參
在移動應用開發中,頁面參數的傳遞也是一個比較常見的需求。為了滿足不同場景下頁面跳轉過程中參數傳遞的需求,Flutter提供了路由參數機制,可以在打開路由時傳遞參數,然后在目標頁面通過RouteSettings來獲取頁面傳遞的參數,如下所示。
Navigator.of(context).pushNamed("second ", arguments: " from first page"); class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { //取出路由參數 String msg = ModalRoute.of(context).settings.arguments as String; … //數據處理 } }
除此之外,對于某些特定的頁面,還需要在其關閉時回傳頁面處理的處理結果。這與Android提供的startActivityForResult()方法監聽目標頁面返回處理結果的場景類似,Flutter也提供了頁面返回的參數機制。具體來說,就是在使用push()方法打開目標頁面時,可以設置目標頁面關閉時監聽函數來獲取返回參數,當目標頁面關閉路由時使用pop()方法回傳參數即可。例如,下面是兩個頁面之間參數值傳遞和參數值回傳,代碼如下。
class FirstPage extends StatefulWidget { @override State<StatefulWidget> createState() { return FirstPageState(); } } class FirstPageState extends State<FirstPage> { String result = ''; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( children: <Widget>[ Text('from seconde page: ' + result, style: TextStyle(fontSize: 20)), RaisedButton( child: Text('跳轉'), //使用then()獲取目標頁面返回參數 onPressed: () => Navigator.of(context) .pushNamed("second", arguments: "from first page") .then((msg) => setState(() => result = msg))) ], )), ); } } class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { String msg = ModalRoute.of(context).settings.arguments as String; return Scaffold( body: Center( child: Column(children: [ Text('from first screen: ' + msg, style: TextStyle(fontSize: 20)), RaisedButton( child: Text('返回'), onPressed: () => Navigator.pop(context, "from second page")) ]), )); } }
運行上面的代碼,可以看到,當SecondPage頁面被關閉重新回到FirstPage頁面時,FirstPage會把回傳的參數值展示出來,最終效果如下圖所示。
MaterialPageRoute
在使用路由過程中,經過會使用到MaterialPageRoute類。MaterialPageRoute繼承自PageRoute類,PageRoute類是一個抽象類,表示占有整個屏幕空間的一個模態路由頁面,它還定義了路由構建及切換時過渡動畫的相關接口及屬性。
MaterialPageRoute 是Material組件庫提供的組件,它可以針對不同平臺,實現與平臺頁面切換動畫風格一致的路由切換動畫:當打開頁面時,新的頁面會從屏幕右側邊緣一致滑動到屏幕左邊,直到新頁面全部顯示到屏幕上,而上一個頁面則會從當前屏幕滑動到屏幕左側而消失;當關閉頁面時,正好相反,當前頁面會從屏幕右側滑出,同時上一個頁面會從屏幕左側滑入。
MaterialPageRoute 構造函數和各個參數的意義如下:
MaterialPageRoute({ @required this.builder, RouteSettings settings, this.maintainState = true, bool fullscreenDialog = false, })
它們的具體含義如下:
總結
Flutter 提供了基本路由和命名路由兩種方式,來管理頁面間的跳轉。其中,基本路由需要自己手動創建頁面實例,通過 Navigator.push 完成頁面跳轉;而命名路由需要提前注冊頁面標識符和頁面創建方法,通過 Navigator.pushNamed 傳入標識符實現頁面跳轉。
對于命名路由,如果我們需要響應錯誤路由標識符,還需要一并注冊 UnknownRoute。為了精細化控制路由切換,Flutter 提供了頁面打開與頁面關閉的參數機制,我們可以在頁面創建和目標頁面關閉時,取出相應的參數。可以看到,關于路由導航,Flutter 綜合了 Android、iOS 和 React 的特點,簡潔而不失強大。
在中大型應用中,通常還會使用命名路由來管理頁面間的切換。命名路由的最重要作用,就是建立了字符串標識符與各個頁面之間的映射關系,使得各個頁面之間完全解耦,應用內頁面的切換只需要通過一個字符串標識符就可以搞定,為后期模塊化打好基礎。
除此之外,嵌套路由和路由傳參也是路由框架中比較核心的內容。本篇只是Flutter路由與導航的基本知識,后面將會從pushReplacementNamed 、 popAndPushNamed、pushNamedAndRemoveUntil和popUntil,以及第三方導航庫和源碼分析等方面來深入介紹Flutter的路由開發與導航。
以上是Flutter實現路由與導航的方法的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。