Navigator——Flutter中的路由管理入門

白瑞德發表於2019-09-16

簡介

路由(Route)在移動開發中通常指頁面(Page),在Android中通常指一個Activity。路由管理,就是管理頁面之間如何跳轉,通常也可被稱為導航管理。在Flutter中,不同頁面之間進行切換和傳送資料,這些頁面被稱為Route(路由),是由一個Navigator的小部件進行管理。Flutter中的路由管理和原生開發類似,導航管理都會維護一個棧,入棧(push)操作對應開啟一個新頁面,出棧(pop)操作對應頁面關閉操作,而路由管理主要是指如何來管理路由棧。

一個簡單的例子:

建立一個Flutter應用,啟動之後點選按鈕跳轉一個新的頁面。

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中註冊路由表:

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中的方法

瞭解了路由的基本使用和跳轉之後,我們來看一下Navigator中基本的方法:

Navigator——Flutter中的路由管理入門

Flutter使用一個堆疊來管理路由,push為入棧,將元素新增到堆疊的頂部,相應的pop為出棧,同一堆疊中刪除頂部元素。在Flutter總,push和pop,已經可以勝任我們大多數的業務場景了。push和pushNamed的基本使用我們已經在上文講過了,Navigator的各個靜態方法的使用基本和這兩者類似。下面我們對其中的每個方法做一個簡單的介紹:

pop & canPop

當我們要結束當前頁面,返回上一頁面時,我們可以使用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。

maybePop & popUntil

maybePop可以理解為canPop的升級版,canPop只用來判斷頁面是否可以被pop。而maybePop則對此進行了升級——如果可以pop則直接pop,否則什麼都不做。

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。

Navigator——Flutter中的路由管理入門
該方法可以幫助我們清除棧內指定頁面之上所有的頁面,然後顯示指定頁面(例如快速回到主頁)。

pushReplacement & pushReplacementNamed

pushReplacementpushReplacementNamed的功能一致,它們二者的區別與pushpushNamed的區別一樣——前者直接將頁面入棧,後者通過路由命名的名字將頁面入棧。這兩對的使用方法也一致,不同的是pushReplacementpushReplacementNamed不是講新的頁面直接入棧,而是替換掉棧頂的頁面。類比Android原生可以理解為:在啟動新的Activity時,finish()掉當前頁面。

Navigator.of(context).pushReplacementNamed('/d');
複製程式碼

Navigator——Flutter中的路由管理入門

pushAndRemoveUntil & pushNamedAndRemoveUntil

同上,兩者的區別不在贅述,它們的實現的功能為:向棧新增新的路由,並刪除所有先前的路由,直到路由為指定的路由為止——例如在退出登入時我們可以直接清除棧內的頁面返回首頁。用法如下:

Navigator.pushNamedAndRemoveUntil(context, "/a",ModalRoute.withName("/a"));
複製程式碼

假如原來的頁面順序為a->b->c->d,執行完上述程式碼後為:a->a。

Navigator——Flutter中的路由管理入門

removeRoute & removeRouteBelow

removeRoute表示從Navigator中刪除路由,同時執行Route.dispose釋放Route自身資源,路由的生命週期結束。 removeRouteBelow從Navigator中刪除路由,同時執行Route.dispose操作,要替換的路由是傳入引數anchorRouter裡面的路由。

replace & replaceRouteBelow

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下好的畫圖軟體推薦啊。

Navigator——Flutter中的路由管理入門

相關文章