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

前言

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

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

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

在Android中,頁面對應(yīng)的是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 相關(guān)的知識點。

路由( Route )在移動開發(fā)中通常指頁面( Page ),這跟 web 開發(fā)中單頁應(yīng)用的 Route 概念意義是相同的,RouteAndroid 中通常指一個 Activity ,在 iOS 中指一個 ViewController 。所謂路由管理,就是管理頁面之間如何跳轉(zhuǎn),通常也可被稱為導(dǎo)航管理。這和原生開發(fā)類似,無論是 Android 還是 iOS ,導(dǎo)航管理都會維護一個路由棧,路由入棧( push )操作對應(yīng)打開一個新頁面,路由出棧( pop)操作對應(yīng)頁面關(guān)閉操作,而路由管理主要是指如何來管理路由棧。

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:默認情況下,當(dāng)入棧一個新路由時,原來的路由仍然會被保存在內(nèi)存中,如果想在路由沒用的時候釋放其所占用的所有資源,可以設(shè)置maintainState為false。
  • fullscreenDialog表示新的路由頁面是否是一個全屏的模態(tài)對話框,在iOS中,如果fullscreenDialogtrue,新頁面將會從屏幕底部滑入(而不是水平方向)。

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

命名路由

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

路由表

要想使用命名路由,我們必須先提供并注冊一個路由表(routing table),這樣應(yīng)用程序才知道哪個名稱與哪個路由Widget對應(yīng)。路由表的定義是一個 Map<String, WidgetBuilder> 結(jié)構(gòu)的 Map , key 為路由的名稱,是個字符串;value是個builder回調(diào)函數(shù),用于生成相應(yīng)的路由Widget。我們在通過路由名稱入棧新路由時,應(yīng)用會根據(jù)路由名稱在路由表中找到對應(yīng)的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集合。通常當(dāng)前屏幕顯示的頁面就是棧頂?shù)穆酚伞?code>Navigator提供了一系列方法來管理路由棧,我們主要使用 pushpop 連個操作進行頁面的入棧和出棧。

push

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

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