Flutter Navigator&Router(導航與路由)

在Android中頁面跳轉使用context.startActivity,iOS中頁面之間跳轉使用的是ViewController。在我們Flutter中,頁面之間的跳轉與數據傳遞使用的是Navigator.pushNavigator.pop以及Router。也是比較簡單的,我們一起來使用Navigator+Router看看如何實現下面的效果。

Flutter navigation-Router.gif

打開新頁面并且返回

實現下面效果,打開新頁面并且返回到上一級頁面。


navigation-router.gif
  • 創建兩個頁面
  • Navigator.push(打開頁面)
  • Navigator.pop(退出當前頁面)

新建兩個頁面


class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FirstPage'),
      ),
      body: SafeArea(
          child: RaisedButton(
              child: Text(" Navigator.push SecondPage"),
              onPressed: () {
               //導航到SecondPage
                _navigateSecondPage(context);
              })),
    );
  }

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SecondPage'),
      ),
      body: SafeArea(
          child: RaisedButton(
              child: Text(" Navigator.push ThirdPage"),
              onPressed: () {
                  //退出當前頁面
                _backCurrentPage(context);
              })),
    );
  }
}

使用Navigator.push 跳轉到新頁面

Navigator.push(BuildContext context, Route<T> route)可以將當前頁面轉換成Router,壓入由Navigator管理的路由堆棧(the stack of routes)中。

Route的具體實現如下:

Route.png

這里我們使用MaterialPageRoute

void _navigateSecondPage(BuildContext context) {

    //MaterialPageRoute({
    //    @required this.builder,
    //    RouteSettings settings,
    //    this.maintainState = true,
    //    bool fullscreenDialog = false,
    //  })
    Navigator.push(context, MaterialPageRoute(builder: (context) {
      return SecondPage();
    }));
  }

使用Navigator.pop退出當前頁面

Navigator.pop(BuildContext context, [ T result ]),會將當前頁面對應的RouterNavigator管理的路由堆棧中移除。


//退出當前頁面,返回到上一級頁面
void _backCurrentPage(BuildContext context) {
  Navigator.pop(context);
}

攜帶數據到新的頁面

發送也就是攜帶數據到新頁面也是十分簡單,我們改造下上面的_navigateSecondPage方法即可。在上面的例子中我們通過return SecondPage(),可以將數據放入到SecondPage的構造方法中,這樣打開新頁面的時候數據就自動帶入到了新頁面。例如,我們攜帶String類型的數據到SecondPage,可以按照下面的方法來弄。

  • 改動SecondPage的構造方法

class SecondPage extends StatelessWidget {
  final value;
  SecondPage({Key key, @required this.value}) : super(key: key);
   @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SecondPage'),
      ),
      body: SafeArea(
          child: RaisedButton(
              child: Text("$value"),
              onPressed: () {
                _backCurrentPage(context);
              })),
    );
  }
}
  • 改動_navigateSecondPage方法

void _navigateSecondPage(BuildContext context) {
    Navigator.push(context, MaterialPageRoute(builder: (context) {
      return SecondPage(value: '我是FirstPage帶來的數據');
    }));
  }

效果如下:

Flutter SendData new Page.gif

對于頁面之間的跳轉除了上面的方法之外,還可以基于命名路由的方式自由跳轉,下節再講。

返回數據到上一級頁面

  • 將要返回的數據放入到Navigator.pop
//退出當前頁面,返回到上一級頁面
void _backCurrentPage(BuildContext context) {
  print('執行了_backCurrentPage');
///只有執行了這個方法,上級頁面才會收到返回的數據
  Navigator.pop(context, '我是來自SecondPage的數據');
}
  • 使用Navigator.push+async+await處理返回的數據
/// async關鍵字聲明該函數內部有代碼需要延遲執行
  ///  使用await會把延遲運算放入到延遲運算的隊列(await)中。
  void _navigateSecondPage(BuildContext context) async {
    print('執行了_navigateSecondPage');
    final result =
        await Navigator.push(context, MaterialPageRoute(builder: (context) {
      return SecondPage();
    }));
    print('FirstPage收到數據:$result');
  }
返回數據.gif

可以看到上一級收到了數據,不過前提是調用了Navigator.pop(context, '我是來自SecondPage的數據');,直接返回上級頁面,這種情況下,上級頁面是收不到數據的。

完整代碼如下:


import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FirstPage'),
      ),
      body: SafeArea(
          child: RaisedButton(
              child: Text("Navigator.push SecondPage"),
              onPressed: () {
                _navigateSecondPage(context);
              })),
    );
  }

//  void _navigateSecondPage(BuildContext context) {
//    //MaterialPageRoute({
//    //    @required this.builder,
//    //    RouteSettings settings,
//    //    this.maintainState = true,
//    //    bool fullscreenDialog = false,
//    //  })
//    Navigator.push(context, MaterialPageRoute(builder: (context) {
//      return SecondPage(value: '我是FirstPage帶來的數據');
//    }));
//  }

  /// async關鍵字聲明該函數內部有代碼需要延遲執行
  ///  使用await會把延遲運算放入到延遲運算的隊列(await)中。
  void _navigateSecondPage(BuildContext context) async {
    print('執行了_navigateSecondPage');
    final result =
        await Navigator.push(context, MaterialPageRoute(builder: (context) {
      return SecondPage();
    }));
    print('FirstPage收到數據:$result');
  }
}

class SecondPage extends StatelessWidget {


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SecondPage'),
      ),
      body: SafeArea(
          child: RaisedButton(
              child: Text("返回數據到FirstPage"),
              onPressed: () {
                _backCurrentPage(context);
              })),
    );
  }
}

///退出當前頁面,返回到上一級頁面
void _backCurrentPage(BuildContext context) {
  print('執行了_backCurrentPage');
  ///只有執行了這個方法,上級頁面才會收到返回的數據
  Navigator.pop(context, '我是來自SecondPage的數據');
}

基于已命名路由調整

除了上述跳轉方式之外,Flutter還支持將路由與頁面對應起來。進行已命名路由的跳轉。這一部分也是比較容易了解的。

import 'package:flutter/material.dart';

class NamedRouter {
  static Map<String, WidgetBuilder> routes;
//初始化App
  static Widget initApp() {
    return MaterialApp(
      initialRoute: '/',
      routes: NamedRouter.initRoutes(),
    );
  }

//初始化路由
  static initRoutes() {
    routes = {
      '/': (context) => FirstScreen(),
      '/second': (context) => SecondScreen()
    };
    return routes;
  }


}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Launch screen'),
          onPressed: () {
            // Navigate to the second screen using a named route
            Navigator.pushNamed(context, '/second');
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Navigate back to the first screen by popping the current route
            // off the stack
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

//main函數中使用
void main() => runApp(NamedRouter.initApp());

Named-Router.gif

回到開頭,看看如何基于上面的知識實戰實現豆瓣Top150詳情。

代碼地址

Flutter navigation-Router.gif

Flutter 豆瓣客戶端,誠心開源
Flutter Container
Flutter SafeArea
Flutter Row Column MainAxisAlignment Expanded
Flutter Image全解析
Flutter 常用按鈕總結
Flutter ListView豆瓣電影排行榜
Flutter Card
Flutter Navigator&Router(導航與路由)
OverscrollNotification不起效果引起的Flutter感悟分享
Flutter 上拉抽屜實現
Flutter 豆瓣客戶端,誠心開源
Flutter 更改狀態欄顏色

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