Flutter路由管理和頁面參數(shù)的傳遞(獲取&返回)

前言

在做 Flutter 開發(fā)的時候所有的頁面以及頁面上的元素都變成了 Widget 五续,創(chuàng)建一個頁面或者視圖直接 new 一個新的 widget 就可以洞难,相關的參數(shù)我們可以直接通過構(gòu)造函數(shù)直接傳遞赘那。

我們做 Android 開發(fā)的人員都知道 Android 應用程序在進行頁面跳轉(zhuǎn)的時候可以利用Intent進行參數(shù)傳遞,那么再開發(fā) Flutter 的時候有類似的方式可以進行參數(shù)傳遞么翔曲?答案當然是有抖韩。

Flutter中文網(wǎng) 中有一段話,大多數(shù)應用程序包含多個頁面敌土。例如镜硕,我們可能有一個顯示產(chǎn)品的頁面,然后返干,用戶可以點擊產(chǎn)品兴枯,跳到該產(chǎn)品的詳情頁。

在Android中矩欠,頁面對應的是Activity财剖,在iOS中是ViewController。而在Flutter中癌淮,頁面只是一個widget躺坟!

在Flutter中,我們那么我們可以使用Navigator在頁面之間跳轉(zhuǎn)乳蓄。

所以我們下邊講述 widget 的參數(shù)傳遞咪橙,從簡單到簡便:

widget構(gòu)造參數(shù)傳遞

route參數(shù)傳遞

上面兩種方式進混合(onGenerateRoute)

widget構(gòu)造參數(shù)傳遞

class Page extends StatelessWidget{
  Page({this.arguments});
  final Map arguments;

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Text("this page name is ${arguments != null ? arguments['name'] : 'null'}"),
      ),
    );
  }
}

上面是一個簡單的 Flutter 的視圖組件,我們在使用參數(shù) arguments 的時候只需要將其傳入到 Page({this.arguments}) 的構(gòu)造函數(shù)中虚倒。

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Page(arguments: {"name": 'Flutter Demo Home Page'}),
    );
  }
}

這種方式進行的參數(shù)傳遞只能單向往下一個頁面?zhèn)鬟f匣摘,不能像Android的 setResult 一樣往上一級頁面?zhèn)鬟f數(shù)據(jù)。

Route

在講 Route 傳參的時候裹刮,我們先講講 FlutterRoute 相關的知識點音榜。

路由( Route )在移動開發(fā)中通常指頁面( Page ),這跟 web 開發(fā)中單頁應用的 Route 概念意義是相同的捧弃,RouteAndroid 中通常指一個 Activity 赠叼,在 iOS 中指一個 ViewController 。所謂路由管理违霞,就是管理頁面之間如何跳轉(zhuǎn)嘴办,通常也可被稱為導航管理。這和原生開發(fā)類似买鸽,無論是 Android 還是 iOS 涧郊,導航管理都會維護一個路由棧,路由入棧( push )操作對應打開一個新頁面眼五,路由出棧( pop)操作對應頁面關閉操作妆艘,而路由管理主要是指如何來管理路由棧彤灶。

MaterialPageRoute

MaterialPageRoute 是我們使用最為廣泛的路由類,它繼承自 PageRoute 類批旺, PageRoute 類是一個抽象類繼承抽象類 ModalRoute幌陕,下面我們介紹一下 MaterialPageRoute 構(gòu)造函數(shù)的各個參數(shù)的意義:

MaterialPageRoute({
  @required this.builder,
  RouteSettings settings,
  this.maintainState = true,
  bool fullscreenDialog = false,
}) : assert(builder != null),
      assert(maintainState != null),
      assert(fullscreenDialog != null),
      assert(opaque),
      super(settings: settings, fullscreenDialog: fullscreenDialog);
  • builder 是一個WidgetBuilder類型的回調(diào)函數(shù),它的作用是構(gòu)建路由頁面的具體內(nèi)容汽煮,返回值是一個widget搏熄。我們通常要實現(xiàn)此回調(diào),返回新路由的實例暇赤。
  • settings 包含路由的配置信息心例,如路由名稱、路由參數(shù)鞋囊、是否初始路由(首頁)契邀。
  • maintainState:默認情況下,當入棧一個新路由時失暴,原來的路由仍然會被保存在內(nèi)存中坯门,如果想在路由沒用的時候釋放其所占用的所有資源,可以設置maintainState為false逗扒。
  • fullscreenDialog表示新的路由頁面是否是一個全屏的模態(tài)對話框古戴,在iOS中,如果fullscreenDialogtrue矩肩,新頁面將會從屏幕底部滑入(而不是水平方向)现恼。

如果想自定義路由切換動畫,可以自己繼承PageRoute來實現(xiàn)黍檩,我們將在后面介紹動畫時叉袍,實現(xiàn)一個自定義的路由Widget。

命名路由

所謂命名路由(Named Route)即給路由起一個名字刽酱,然后可以通過路由名字直接打開新的路由喳逛。這為路由管理帶來了一種直觀、簡單的方式棵里。和 Android 中的 ARrouter 頁面跳轉(zhuǎn)框架所定義的 path 非常的類似润文。

路由表

要想使用命名路由,我們必須先提供并注冊一個路由表(routing table)殿怜,這樣應用程序才知道哪個名稱與哪個路由Widget對應典蝌。路由表的定義是一個 Map<String, WidgetBuilder> 結(jié)構(gòu)的 Map , key 為路由的名稱头谜,是個字符串骏掀;value是個builder回調(diào)函數(shù),用于生成相應的路由Widget。我們在通過路由名稱入棧新路由時截驮,應用會根據(jù)路由名稱在路由表中找到對應的WidgetBuilder回調(diào)函數(shù)笑陈,然后調(diào)用該回調(diào)函數(shù)生成路由widget并返回。

我們在創(chuàng)建 MaterialApp 的時候就有一個 routes 構(gòu)造參數(shù):

const MaterialApp({
  Key key,
  this.navigatorKey,
  this.home,
  this.routes = const <String, WidgetBuilder>{},
  this.initialRoute,
  this.onGenerateRoute,
  this.onUnknownRoute,
  this.navigatorObservers = const <NavigatorObserver>[],
  /***********/
})

Navigator

Navigator是一個路由管理的widget侧纯,它通過一個棧來管理一個路由widget集合。通常當前屏幕顯示的頁面就是棧頂?shù)穆酚伞?code>Navigator提供了一系列方法來管理路由棧甲脏,我們主要使用 pushpop 連個操作進行頁面的入棧和出棧眶熬。

push

將給定的路由入棧(即打開新的頁面),返回值是一個Future對象块请,用以接收新路由出棧(即關閉)時的返回數(shù)據(jù)娜氏。

push 我們主要使用兩個方法一個是直接 push 一個路由,另外一個是 pushNamed 一個命名路由地址(PS:要想使用命名路由必須提供并注冊一個路由表墩新,這后面會講到)贸弥。

push方法源碼

下邊是 Navigator.push 的源碼,入?yún)⒌?Route 對象中有一個 RouteSettings 成員變量海渊,我們可以在構(gòu)造 Route 對象的時候?qū)⑿枰獋鬟f的參數(shù)放在 RouteSettings 中绵疲。

@optionalTypeArgs
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
  return Navigator.of(context).push(route);
}

push方法使用

我們可以將參數(shù)放在 SecondScreen 的構(gòu)造函數(shù)中,也可以放在構(gòu)造的 MaterialPageRouteRouteSettings 中臣疑。

Navigator.push(
  context,
  new MaterialPageRoute(builder: (context) => new SecondScreen()),
).then((data){
  //接受返回的參數(shù)
  print(data.toString());
};

pushNamed方法源碼

第二種方式最終的實現(xiàn)也是調(diào)用的 push 方法盔憨,這中方法直接暴露了參數(shù) Object arguments

@optionalTypeArgs
static Future<T> pushNamed<T extends Object>(
  BuildContext context,
  String routeName, {
  Object arguments,
  }) {
  return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
@optionalTypeArgs
Future<T> pushNamed<T extends Object>(
  String routeName, {
  Object arguments,
}) {
  return push<T>(_routeNamed<T>(routeName, arguments: arguments));
}

pushNamed方法使用

使用前提是 /route1 已經(jīng)被注冊到路由表中:

Navigator.of(context)
  .pushNamed(
    '/route1',
    arguments: {
      "name": 'hello'
    }
    ).then((data){
    //接受返回的參數(shù)
    print(data.toString());
    };

pop

將棧頂路由出棧讯沈,入?yún)橐粋€ object 類型的對象為當前頁面關閉時返回給上一個頁面的數(shù)據(jù)郁岩。

@optionalTypeArgs
static bool pop<T extends Object>(BuildContext context, [ T result ]) {
  return Navigator.of(context).pop<T>(result);
}

使用非常簡單:

Navigator.of(context).pop("ok~!");

頁面參數(shù)的傳輸、獲取以及結(jié)果返回

參數(shù)傳輸

Navigator.of(context).pushNamed('/route1', arguments: {"name": 'hello'});

參數(shù)獲取

class Page extends StatelessWidget{
  String name;
  @override
  Widget build(BuildContext context) {
    dynamic obj = ModalRoute.of(context).settings.arguments;
    if (obj != null && isNotEmpty(obj["name"])) {
      name = obj["name"];
    }
    return Material(
      child: Center(
        child: Text("this page name is ${name}"),
      ),
    );
  }
}

參數(shù)返回

//頁面返回參數(shù)
Navigator.of(context).pop("ok~!");
//上一個頁面接收參數(shù)
Navigator.of(context)
  .pushNamed(
    '/route1',
    arguments: {
      "name": 'hello'
    }
    ).then((data){
    //接受返回的參數(shù)
    print(data.toString());
    };

onGenerateRoute構(gòu)建路由

在說 onGenerateRoute 構(gòu)建路由之前缺狠,我們得先了解他问慎。前面 MaterialApp 的的構(gòu)造函數(shù)中我們看到過它出現(xiàn), MaterialApp 有一個參數(shù)類型為 Function 類型的 onGenerateRoute 挤茄。

void main() => runApp(MyApp());
Map<String, WidgetBuilder> routers = {'/route1': (context, {arguments}) => Page(arguments: arguments)}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      // 處理Named頁面跳轉(zhuǎn) 傳遞參數(shù)
      onGenerateRoute: (RouteSettings settings) {
        // 統(tǒng)一處理
        final String name = settings.name;
        final Function pageContentBuilder = routers[name];
        if (pageContentBuilder != null) {
          final Route route =
          MaterialPageRoute(
                builder: (context) {
                  //將RouteSettings中的arguments參數(shù)取出來如叼,通過構(gòu)造函數(shù)傳入
                  return pageContentBuilder(context, arguments: settings.arguments);
                },
                settings: settings,
            );
          return route;
        }
      },
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(name: 'Flutter Demo Home Page'),
      //routes優(yōu)先執(zhí)行,所以必須注釋掉穷劈,否則onGenerateRoute方法不會調(diào)用
      //routes: routers,
    );
  }
}
class Page extends StatelessWidget{
  Page({this.arguments});
  final Map arguments;
  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Text("this page name is ${arguments != null ? arguments['name'] : 'null'}"),
      ),
    );
  }
}
  • 這種方式統(tǒng)一處理了頁面的 arguments 參數(shù)薇正,所以必須保證 Map<String, WidgetBuilder> routers 當中注冊的所有 Widget 的構(gòu)造函數(shù)中都有一個 Map 類型并且名為 arguments 的參數(shù)。
  • 這種方法同時也傳遞了 RouteSettings 囚衔,所以在下一個頁面我們也可以通過 ModalRoute.of(context).settings.arguments 方式獲取參數(shù)挖腰。
  • 這種方式可以自定義 PageRoute 的類型,比如自帶 IOS 側(cè)滑返回效果的 CupertinoPageRoute 等练湿。之前通過在 WidgetsApp 注冊routes 的方式默認生成的 PageRoute 類型為 MaterialPageRoute 猴仑。

源碼分析傳送門:Flutter路由管理和頁面參數(shù)的傳遞(源碼分析)

文章到這里就全部講述完啦,若有其他需要交流的可以留言哦辽俗!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疾渣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子崖飘,更是在濱河造成了極大的恐慌榴捡,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朱浴,死亡現(xiàn)場離奇詭異吊圾,居然都是意外死亡,警方通過查閱死者的電腦和手機翰蠢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門项乒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梁沧,你說我怎么就攤上這事檀何。” “怎么了廷支?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵频鉴,是天一觀的道長。 經(jīng)常有香客問我恋拍,道長砚殿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任芝囤,我火速辦了婚禮似炎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘悯姊。我一直安慰自己羡藐,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布悯许。 她就那樣靜靜地躺著仆嗦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪先壕。 梳的紋絲不亂的頭發(fā)上瘩扼,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音垃僚,去河邊找鬼集绰。 笑死,一個胖子當著我的面吹牛谆棺,可吹牛的內(nèi)容都是我干的栽燕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碍岔!你這毒婦竟也來了浴讯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蔼啦,失蹤者是張志新(化名)和其女友劉穎榆纽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捏肢,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡奈籽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了猛计。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唠摹。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡爆捞,死狀恐怖奉瘤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情煮甥,我是刑警寧澤盗温,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站成肘,受9級特大地震影響卖局,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜双霍,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一砚偶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洒闸,春花似錦染坯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至深纲,卻和暖如春仲锄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湃鹊。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工儒喊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人币呵。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓澄惊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子掸驱,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容