路由(Route)在移動開發中一般指頁面(Page),在Android中一般指一個Activity。路由管理,就是管理頁面之間如何跳轉,一般也可被稱爲導航管理。在Flutter中,不一樣頁面之間進行切換和發送數據,這些頁面被稱爲Route(路由),是由一個Navigator的小部件進行管理。Flutter中的路由管理和原生開發相似,導航管理都會維護一個棧,入棧(push)操做對應打開一個新頁面,出棧(pop)操做對應頁面關閉操做,而路由管理主要是指如何來管理路由棧。bash
建立一個Flutter應用,啓動以後點擊按鈕跳轉一個新的頁面。markdown
class HomePage extends StatefulWidget { @override _HomePage createState() { // TODO: implement createState return _HomePage(); } } class _HomePage extends State<HomePage> { @override Widget build(BuildContext context) { // TODO: implement build return Center( child: GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) { return PageA(); }, ), ); }, child: Text("HomePage"), ), ); } } class PageA extends StatefulWidget { @override _PageA createState() { // TODO: implement createState return _PageA(); } } class _PageA extends State<PageA> { @override Widget build(BuildContext context) { // TODO: implement build return Center( child: Text("PageA"), ); } } 複製代碼
要想使用命名路由,咱們必須先提供並註冊一個路由表(routing table),這樣應用程序才知道哪一個名字與哪一個路由組件相對應。其實註冊路由表就是給路由起名字,路由表的定義以下: Map<String, WidgetBuilder> routes;
它是一個Map,key爲路由的名字,是個字符串;value是個builder回調函數,用於生成相應的路由widget。咱們在經過路由名字打開新路由時,應用會根據路由名字在路由表中查找到對應的WidgetBuilder回調函數,而後調用該回調函數生成路由widget並返回。 咱們能夠在MaterialApp
中註冊路由表:app
class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), routes: { "/home":(context)=>HomePage(), "/a":(context)=>PageA(), "/b":(context)=>PageB(), "/c":(context)=>PageC(), }, ); } } 複製代碼
瞭解了路由的基本使用和跳轉以後,咱們來看一下Navigator中基本的方法: less
Flutter使用一個堆棧來管理路由,push爲入棧,將元素添加到堆棧的頂部,相應的pop爲出棧,同一堆棧中刪除頂部元素。在Flutter總,push和pop,已經能夠勝任咱們大多數的業務場景了。push和pushNamed的基本使用咱們已經在上文講過了,Navigator的各個靜態方法的使用基本和這二者相似。下面咱們對其中的每一個方法作一個簡單的介紹:ide
當咱們要結束當前頁面,返回上一頁面時,咱們可使用pop讓路由出棧: Navigator.pop(context);
若是須要有返回參數給上一頁面: Navigator.pop(context,"返回的數據");
相應的,須要在修改跳轉的:函數
Navigator.push( context, MaterialPageRoute( builder: (context) { return PageA( name: "wall", age: "13", ); }, ), ).then((v) { //接收目標頁面返回的參數 setState(() { a = v; }); }); 複製代碼
在then裏接收第二個頁面返回的參數,並使用setState刷新頁面 canPop能夠用來判斷頁面是否被彈出,根據官方的教程,初始化路由頁面沒法被pop,也就是說當棧內只有一個路由時,改方法會返回false,其餘時刻返回true。ui
maybePop
能夠理解爲canPop的升級版,canPop只用來判斷頁面是否能夠被pop。而maybePop則對此進行了升級——若是能夠pop則直接pop,不然什麼都不作。this
if (Navigator.canPop(context)) { Navigator.pop(context); } else { //nothing } 複製代碼
也就是說當前頁面能返回就返回,返回不了就拉倒。 popUntil
做用是反覆執行pop 直到返回到咱們指定的頁面爲止,popUntil接收一個函數,等到該函數的參數predicate返回true爲結束pop操做: Navigator.popUntil(context, ModalRoute.withName('/a'));
假如原來的頁面順序爲a->b->c->d,執行完上述代碼後爲:a。 spa
pushReplacement
和pushReplacementNamed
的功能一致,它們兩者的區別與push
和pushNamed
的區別同樣——前者直接將頁面入棧,後者經過路由命名的名字將頁面入棧。這兩對的使用方法也一致,不一樣的是pushReplacement
和pushReplacementNamed
不是講新的頁面直接入棧,而是替換掉棧頂的頁面。類比Android原生能夠理解爲:在啓動新的Activity時,finish()掉當前頁面。code
Navigator.of(context).pushReplacementNamed('/d'); 複製代碼
同上,二者的區別不在贅述,它們的實現的功能爲:向棧添加新的路由,並刪除全部先前的路由,直到路由爲指定的路由爲止——例如在退出登陸時咱們能夠直接清除棧內的頁面返回首頁。用法以下:
Navigator.pushNamedAndRemoveUntil(context, "/a",ModalRoute.withName("/a")); 複製代碼
假如原來的頁面順序爲a->b->c->d,執行完上述代碼後爲:a->a。
removeRoute
表示從Navigator中刪除路由,同時執行Route.dispose釋放Route自身資源,路由的生命週期結束。 removeRouteBelow
從Navigator中刪除路由,同時執行Route.dispose操做,要替換的路由是傳入參數anchorRouter裏面的路由。
replace
將Navigator中的路由替換成一個新路由。 replaceRouteBelow
將Navigator中的路由替換成一個新路由,要替換的路由是是傳入參數anchorRouter裏面的路由。
普通路由中的傳參以下所示:
Navigator.push(context, MaterialPageRoute(builder: (context) => Less(name: "new"))); //在新的Widget的構造方法裏接受參數: class Less extends StatelessWidget { final String name; Less({Key key, this.name}) : super(key: key){ print("無狀態組件$name:建立了"); } } 複製代碼
對於頁面數據的回調,除了上文中講的,還可使用方法傳遞的方式實現:直接將父Widget的方法傳遞給子Widget,在新的Widget裏調用傳遞過來的方法更新數據。 命名路由的傳參有些許複雜:
//攜帶參數跳轉 Navigator.pushNamed(context, "/a", arguments: {"name": "name"}); //在目標頁面取值 Map arguments2 = ModalRoute.of(context).settings.arguments; 複製代碼
還有一種方案是在 onGenerateRoute 回調中利用URL參數自行處理。 路由跳轉時寫入參數:
onGenerateRoute: (RouteSettings settings) { WidgetBuilder builder; if (settings.name == '/a') { builder = (BuildContext context) => new Full(); } return new MaterialPageRoute(builder: builder, settings: settings); }, 複製代碼
差很少絮叨完了,基本上只是方法羅列,進一步的探索和封轉我也還在探索之中。最後,畫圖真難,誰有Mac下好的畫圖軟件推薦啊。