Flutter學習筆記30-路由管理

路由(Route)在移動開發中通常指頁面(Page),Route在Android中通常指一個Activity,在iOS中指一個ViewController。路由管理,就是管理頁面之間如何跳轉。Flutter中的路由管理和原生開發類似,無論是Android還是iOS,導航管理都會維護一個路由棧,路由入棧(push)操作對應打開一個新頁面,路由出棧(pop)操作對應頁面關閉操作,而路由管理主要是指如何來管理路由棧。

MaterialPageRoute

MaterialPageRoute繼承自PageRoute,PageRoute是一個抽象類,表示占有整個屏幕空間的一個模態路由頁面,它還定義了路由構建及切換時過渡動畫的相關接口及屬性。MaterialPageRouteMaterial組件庫提供的組件,它可以針對不同平臺,實現與平臺頁面切換動畫風格一致的路由切換動畫。對于Android,當打開新頁面時,新的頁面會從屏幕底部滑動到屏幕頂部;當關閉頁面時,當前頁面會從屏幕頂部滑動到屏幕底部后消失,同時上一個頁面會顯示到屏幕上。
對于iOS,當打開頁面時,新的頁面會從屏幕右側邊緣一致滑動到屏幕左邊,直到新頁面全部顯示到屏幕上,而上一個頁面則會從當前屏幕滑動到屏幕左側而消失;當關閉頁面時,正好相反,當前頁面會從屏幕右側滑出,同時上一個頁面會從屏幕左側滑入。

MaterialPageRoute構造函數:

  MaterialPageRoute({
    WidgetBuilder builder,
    RouteSettings settings,
    bool maintainState = true,
    bool fullscreenDialog = false,
  })
  • builder
    一個WidgetBuilder類型的回調函數,它的作用是構建路由頁面的具體內容,返回值是一個widget。通常要實現此回調,返回新路由的實例。
  • settings
    包含路由的配置信息,如路由名稱、是否初始路由(首頁)。
  • maintainState
    默認情況下,當入棧一個新路由時,原來的路由仍然會被保存在內存中,如果想在路由沒用的時候釋放其所占用的所有資源,可以設置maintainState為false。
  • fullscreenDialog
    表示新的路由頁面是否是一個全屏的模態對話框,在iOS中,如果fullscreenDialog為true,新頁面將會從屏幕底部滑入(而不是水平方向)。
Navigator

Navigator是一個路由管理的組件,它提供了打開和退出路由頁方法。Navigator通過一個棧來管理活動路由集合。通常當前屏幕顯示的頁面就是棧頂的路由。Navigator提供了一系列方法來管理路由棧,最常用的兩個方法:

  • Future push(BuildContext context, Route route)
    將給定的路由入棧(即打開新的頁面),返回值是一個Future對象,用以接收新路由出棧(即關閉)時的返回數據。
  • bool pop(BuildContext context, [ result ])
    將棧頂路由出棧,result為頁面關閉時返回給上一個頁面的數據。

Navigator還有很多其它方法,詳情請參考文檔。

路由傳值

很多時候,在路由跳轉或者返回上一級時需要帶一些參數。代碼示例:

class FirstPage extends StatefulWidget {
  @override
  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  String _text = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('第一頁'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('返回傳參:$_text'),
            RaisedButton(
              onPressed: () => _goToSecondPage(context),
              child: Text('下一頁'),
            ),
          ],
        ),
      ),
    );
  }

  void _goToSecondPage(BuildContext context) {
    Navigator.push(context, MaterialPageRoute(builder: (context) {
      return SecondPage(text: '第二頁');
    })).then((value) {
      setState(() {
        _text = value;
      });
    });
  }
}

class SecondPage extends StatelessWidget {
  SecondPage({
    Key key,
    this.text,
  }) : super(key: key);

  final String text;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('第二頁'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(text),
            RaisedButton(
              onPressed: () => Navigator.pop(context, '返回值'),
              child: Text('返回'),
            ),
          ],
        ),
      ),
    );
  }
}

在SecondPage中有兩種方式可以返回到上一頁;第一種方式時直接點擊導航欄返回箭頭,第二種方式是點擊頁面中的“返回”按鈕。這兩種返回方式的區別是前者不會返回數據給上一個路由,而后者會。如果點擊頁面中的返回按鈕也想返回數據,有兩種方法:

  1. 自定義返回的按鈕
appBar: AppBar(
  title: Text("第二頁"),
  leading: IconButton(
    icon: Icon(Icons.arrow_back_ios),
    onPressed: () {
      Navigator.of(context).pop("返回值");
    },
  ),
),
  1. 監聽返回按鈕點擊(給Scaffold包裹一個WillPopScope)
    WillPopScope有一個onWillPop的回調函數,當點擊返回按鈕時會執行,這個函數要求有一個Future的返回值:
    true:系統會自動執行pop操作
    false:系統不再執行pop操作,需要自己來執行
return WillPopScope(
   onWillPop: () {
     Navigator.pop(context, '返回值');
     return Future.value(false);
   },
   child: Scaffold(
     appBar: AppBar(
       title: Text('第二頁'),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
           Text(text),
           RaisedButton(
             onPressed: () => Navigator.pop(context, '返回值'),
             child: Text('返回'),
           ),
         ],
       ),
     ),
   ),
 );
命名路由

命名路由(Named Route)即有名字的路由,可以先給路由起一個名字,然后就可以通過路由名字直接打開新的路由了。

路由表

要想使用命名路由,必須先提供并注冊一個路由表(routing table),這樣應用程序才知道哪個名字與哪個路由組件相對應。路由表的定義如下:

Map<String, WidgetBuilder> routes;

它是一個Map,key為路由的名字,是個字符串;value是個builder回調函數,用于生成相應的路由widget。在通過路由名字打開新路由時,應用會根據路由名字在路由表中查找到對應的 WidgetBuilder回調函數,然后調用該回調函數生成路由widget并返回。

注冊路由表

路由表的注冊方式很簡單,在build方法中找到MaterialApp,添加routes屬性。代碼示例:

MaterialApp(
  title: 'Route Demo',
  theme: ThemeData(
    primarySwatch: Colors.blue,
  ),
  // 注冊路由表
  routes:{
   'secondPage': (context) => SecondPage(),
   // 省略其它路由注冊信息
  } ,
  home: FirstPage(),
);

如果也想將home注冊為命名路由,只需在路由表中注冊一下FirstPage路由,然后將其名字作為MaterialAppinitialRoute屬性值即可。代碼示例:

MaterialApp(
  title: 'Route Demo',
  theme: ThemeData(
    primarySwatch: Colors.blue,
  ),
  // 名為"/"的路由作為應用的home(首頁)
  initialRoute: '/',
  // 注冊路由表
  routes:{
   '/': (context) => FirstPage(),
   'secondPage': (context) => SecondPage(),
   // 省略其它路由注冊信息
  } ,
);
通過路由名打開新路由頁

要通過路由名稱來打開新路由,可以使用Navigator 的pushNamed方法:

Future pushNamed(BuildContext context, String routeName,{Object arguments})

代碼示例:

void _goToSecondPage(BuildContext context) {
  Navigator.pushNamed(context, 'secondPage').then((value) {
    setState(() {
      _text = value;
    });
  });
}

依然可以打開新的路由頁,同樣可以接收返回參數。

命名路由參數傳遞

在路由頁通過RouteSetting對象獲取路由參數:

class SecondPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // 獲取路由參數  
    var args = ModalRoute.of(context).settings.arguments;
    // 省略無關代碼
  }
}

在打開路由時傳遞參數:

Navigator.of(context).pushNamed("secondPage", arguments: "第二頁");

如果想將上面路由傳參示例中的SecondPage路由頁注冊到路由表中,以便也可以通過路由名來打開它。但是,由于SecondPage接受一個text參數,可以不改變SecondPage源碼的前提下適配這種情況:

MaterialApp(
  // 省略無關代碼
  routes: {
    'secondPage': (context) => SecondPage(text: ModalRoute.of(context).settings.arguments),
 }, 
);

代碼傳送門

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容