Flutter開發(fā)(15)- 路由導(dǎo)航

我們通常會用屏(Screen)來稱呼一個頁面(Page),一個完整的App應(yīng)該是有多個Page組成的无宿。

在之前的案例(豆瓣)中,我們通過IndexedStack來管理了首頁中的Page切換:

????首頁-書影音-小組-市集-我的

????通過點擊BottomNavigationBarItem來設(shè)置IndexedStack的index屬性來切換

除了上面這種管理頁面的方式者填,我們還需要實現(xiàn)其它功能的頁面跳轉(zhuǎn):比如點擊一個商品跳轉(zhuǎn)到詳情頁刻伊,某個按鈕跳轉(zhuǎn)到發(fā)送朋友圈、微博的編輯頁面描验。

這種頁面的管理和導(dǎo)航白嘁,我們通常會使用路由進行統(tǒng)一管理。

一. 路由管理

1.1. 認識Flutter路由

路由的概念由來已久膘流,包括網(wǎng)絡(luò)路由絮缅、后端路由,到現(xiàn)在廣為流行的前端路由呼股。

????無論路由的概念如何應(yīng)用耕魄,它的核心是一個路由映射表

????比如:名字 detail 映射到 DetailPage 頁面等

????有了這個映射表之后,我們就可以方便的根據(jù)名字來完成路由的轉(zhuǎn)發(fā)(在前端表現(xiàn)出來的就是頁面跳轉(zhuǎn))

在Flutter中彭谁,路由管理主要有兩個類:Route和Navigator

1.2. Route

Route:一個頁面要想被路由統(tǒng)一管理吸奴,必須包裝為一個Route

????官方的說法很清晰:An abstraction for an entry managed by a Navigator.

但是Route是一個抽象類,所以它是不能實例化的

????在上面有一段注釋缠局,讓我們查看MaterialPageRoute來使用

/// See [MaterialPageRoute] for a route that replaces the

/// entire screen with a platform-adaptive transition.

abstractclass Route<T> {

}

事實上MaterialPageRoute并不是Route的直接子類:

????MaterialPageRoute在不同的平臺有不同的表現(xiàn)

????對Android平臺则奥,打開一個頁面會從屏幕底部滑動到屏幕的頂部,關(guān)閉頁面時從頂部滑動到底部消失

????對iOS平臺狭园,打開一個頁面會從屏幕右側(cè)滑動到屏幕的左側(cè)读处,關(guān)閉頁面時從左側(cè)滑動到右側(cè)消失

????當然,iOS平臺我們也可以使用CupertinoPageRoute

MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route

1.3. Navigator

Navigator:管理所有的Route的Widget唱矛,通過一個Stack來進行管理的

????官方的說法也很清晰:A widget that manages a set of child widgets with a stack discipline.

那么我們開發(fā)中需要手動去創(chuàng)建一個Navigator嗎罚舱?

????并不需要井辜,我們開發(fā)中使用的MaterialApp、CupertinoApp管闷、WidgetsApp它們默認是有插入Navigator的

????所以粥脚,我們在需要的時候,只需要直接使用即可

Navigator.of(context)

Navigator有幾個最常見的方法:

// 路由跳轉(zhuǎn):傳入一個路由對象

Future<T> push<T extendsObject>(Route<T> route)

// 路由跳轉(zhuǎn):傳入一個名稱(命名路由)

Future<T> pushNamed<T extendsObject>(

? String routeName, {

? ? Object arguments,

? })

// 路由返回:可以傳入一個參數(shù)

bool pop<T extendsObject>([ T result ])

二. 路由基本使用

1.1. 基本跳轉(zhuǎn)

我們來實現(xiàn)一個最基本跳轉(zhuǎn):

????創(chuàng)建首頁頁面渐北,中間添加一個按鈕阿逃,點擊按鈕跳轉(zhuǎn)到詳情頁面

????創(chuàng)建詳情頁面,中間添加一個按鈕赃蛛,點擊按鈕返回到首頁頁面

核心的跳轉(zhuǎn)代碼如下(首頁中代碼):

// RaisedButton代碼(只貼出核心代碼)

RaisedButton(

? child: Text("打開詳情頁"),

? onPressed: () => _onPushTap(context),

),

// 按鈕點擊執(zhí)行的代碼

_onPushTap(BuildContext context) {

? Navigator.of(context).push(MaterialPageRoute(

? ? builder: (ctx) {

? ? ? return DetailPage();

? ? }

? ));

}

核心的返回代碼如下(詳情頁中代碼):

// RaisedButton代碼(只貼出核心代碼)

RaisedButton(

? child: Text("返回首頁"),

? onPressed: () => _onBackTap(context),

)

// 按鈕點擊執(zhí)行的代碼

_onBackTap(BuildContext context) {

? Navigator.of(context).pop();

}

1.2. 參數(shù)傳遞

在跳轉(zhuǎn)過程中恃锉,我們通常可能會攜帶一些參數(shù)呕臂,比如

????首頁跳到詳情頁破托,攜帶一條信息:a home message

????詳情頁返回首頁,攜帶一條信息:a detail message

首頁跳轉(zhuǎn)核心代碼:

????在頁面跳轉(zhuǎn)時歧蒋,會返回一個Future

????該Future會在詳情頁面調(diào)用pop時土砂,回調(diào)對應(yīng)的then函數(shù),并且會攜帶結(jié)果

_onPushTap(BuildContext context) {

? // 1.跳轉(zhuǎn)代碼

? final future = Navigator.of(context).push(MaterialPageRoute(

? ? builder: (ctx) {

? ? ? return DetailPage("a home message");

? ? }

? ));

? // 2.獲取結(jié)果

? future.then((res) {

? ? setState(() {

? ? ? _message = res;

? ? });

? });

}

詳情頁返回核心代碼:

_onBackTap(BuildContext context) {

? Navigator.of(context).pop("a detail message");

}

1.3. 返回細節(jié)

但是這里有一個問題谜洽,如果用戶是點擊右上角的返回按鈕萝映,如何監(jiān)聽呢?

方法一:自定義返回的按鈕(在詳情頁中修改Scaffold的appBar)

appBar: AppBar(

? title: Text("詳情頁"),

? leading: IconButton(

? ? icon: Icon(Icons.arrow_back),

? ? onPressed: () {

? ? ? Navigator.of(context).pop("a back detail message");

? ? },

? ),

),

方法二:監(jiān)聽返回按鈕的點擊(給Scaffold包裹一個WillPopScope)

WillPopScope有一個onWillPop的回調(diào)函數(shù)阐虚,當我們點擊返回按鈕時會執(zhí)行

這個函數(shù)要求有一個Future的返回值:

????true:那么系統(tǒng)會自動幫我們執(zhí)行pop操作

????false:系統(tǒng)不再執(zhí)行pop操作序臂,需要我們自己來執(zhí)行

return WillPopScope(

? onWillPop: () {

? ? Navigator.of(context).pop("a back detail message");

? ? return Future.value(false);

? },

? child: Scaffold(

? ? appBar: AppBar(

? ? ? title: Text("詳情頁"),

? ? ),

? ? body: Center(

? ? ? child: Column(

? ? ? ? mainAxisAlignment: MainAxisAlignment.center,

? ? ? ? children: <Widget>[

? ? ? ? ? RaisedButton(

? ? ? ? ? ? child: Text("返回首頁"),

? ? ? ? ? ? onPressed: () => _onBackTap(context),

? ? ? ? ? ),

? ? ? ? ? Text(_message, style: TextStyle(fontSize: 20, color: Colors.red),)

? ? ? ? ],

? ? ? ),

? ? ),

? ),

);

三. 命名路由使用

3.1. 基本跳轉(zhuǎn)

我們可以通過創(chuàng)建一個新的Route,使用Navigator來導(dǎo)航到一個新的頁面实束,但是如果在應(yīng)用中很多地方都需要導(dǎo)航到同一個頁面(比如在開發(fā)中奥秆,首頁、推薦咸灿、分類頁都可能會跳到詳情頁)构订,那么就會存在很多重復(fù)的代碼。

在這種情況下避矢,我們可以使用命名路由(named route)

????命名路由是將名字和路由的映射關(guān)系悼瘾,在一個地方進行統(tǒng)一的管理

????有了命名路由,我們可以通過Navigator.pushNamed() 方法來跳轉(zhuǎn)到新的頁面

命名路由在哪里管理呢审胸?可以放在MaterialApp的 initialRoute 和 routes 中

????initialRoute:設(shè)置應(yīng)用程序從哪一個路由開始啟動分尸,設(shè)置了該屬性,就不需要再設(shè)置home屬性了

????routes:定義名稱和路由之間的映射關(guān)系歹嘹,類型為Map<String, WidgetBuilder>

修改MaterialApp中的代碼:

return MaterialApp(

? title: 'Flutter Demo',

? theme: ThemeData(

? ? primarySwatch: Colors.blue, splashColor: Colors.transparent

? ),

? initialRoute: "/",

? routes: {

? ? "/home": (ctx) => HYHomePage(),

? ? "/detail": (ctx) => HYDetailPage()

? },

);

修改跳轉(zhuǎn)的代碼:

_onPushTap(BuildContext context) {

? Navigator.of(context).pushNamed("/detail");

}

在開發(fā)中,為了讓每個頁面對應(yīng)的routeName統(tǒng)一孔庭,我們通常會在每個頁面中定義一個路由的常量來使用:

class HYHomePage extends StatefulWidget {

? staticconstString routeName = "/home";

}

class HYDetailPage extends StatelessWidget {

? staticconstString routeName = "/detail";

}

修改MaterialApp中routes的key

initialRoute: HYHomePage.routeName,

routes: {

? HYHomePage.routeName: (ctx) => HYHomePage(),

? HYDetailPage.routeName: (ctx) => HYDetailPage()

},

3.2. 參數(shù)傳遞

因為通常命名路由尺上,我們會在定義路由時材蛛,直接創(chuàng)建好對象,比如HYDetailPage()

那么怎抛,命名路由如果有參數(shù)需要傳遞呢卑吭?

pushNamed時,如何傳遞參數(shù):

_onPushTap(BuildContext context) {

? Navigator.of(context).pushNamed(HYDetailPage.routeName, arguments: "a home message of naned route");

}

在HYDetailPage中马绝,如何獲取到參數(shù)呢豆赏?

在build方法中ModalRoute.of(context)可以獲取到傳遞的參數(shù)

? Widget build(BuildContext context) {

? ? // 1.獲取數(shù)據(jù)

? ? final message = ModalRoute.of(context).settings.arguments;

? }

3.3. 路由鉤子

3.3.1. onGenerateRoute

假如我們有一個HYAboutPage,也希望在跳轉(zhuǎn)時富稻,傳入對應(yīng)的參數(shù)message掷邦,并且已經(jīng)有一個對應(yīng)的構(gòu)造方法

在HYHomePage中添加跳轉(zhuǎn)的代碼:

RaisedButton(

? child: Text("打開關(guān)于頁"),

? onPressed: () {

? ? Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message");

? },

)

HYAboutPage的代碼:

class HYAboutPage extends StatelessWidget {

? staticconstString routeName = "/about";

? finalString message;

? HYAboutPage(this.message);

? @override

? Widget build(BuildContext context) {

? ? return Scaffold(

? ? ? appBar: AppBar(

? ? ? ? title: Text("關(guān)于頁面"),

? ? ? ),

? ? ? body: Center(

? ? ? ? child: Text(message, style: TextStyle(fontSize: 30, color: Colors.red),),

? ? ? ),

? ? );

? }

}

但是我們繼續(xù)使用routes中的映射關(guān)系,就不好進行配置了椭赋,因為HYAboutPage必須要求傳入一個參數(shù)抚岗;

這個時候我們可以使用onGenerateRoute的鉤子函數(shù):

????當我們通過pushNamed進行跳轉(zhuǎn),但是對應(yīng)的name沒有在routes中有映射關(guān)系哪怔,那么就會執(zhí)行onGenerateRoute鉤子函數(shù)宣蔚;

????我們可以在該函數(shù)中,手動創(chuàng)建對應(yīng)的Route進行返回认境;

????該函數(shù)有一個參數(shù)RouteSettings胚委,該類有兩個常用的屬性:

????????name: 跳轉(zhuǎn)的路徑名稱

????????arguments:跳轉(zhuǎn)時攜帶的參數(shù)

onGenerateRoute: (settings) {

? if (settings.name == "/about") {

? ? return MaterialPageRoute(

? ? ? builder: (ctx) {

? ? ? ? return HYAboutPage(settings.arguments);

? ? ? }

? ? );

? }

? returnnull;

},

關(guān)于頁面跳轉(zhuǎn)

3.3.2. onUnknownRoute

如果我們打開的一個路由名稱是根本不存在,這個時候我們希望跳轉(zhuǎn)到一個統(tǒng)一的錯誤頁面叉信。

比如下面的abc是不存在有對應(yīng)的頁面的

????如果沒有進行特殊的處理亩冬,那么Flutter會報錯。

RaisedButton(

? child: Text("打開未知頁面"),

? onPressed: () {

? ? Navigator.of(context).pushNamed("/abc");

? },

)

我們可以創(chuàng)建一個錯誤的頁面:

class UnknownPage extends StatelessWidget {

? @override

? Widget build(BuildContext context) {

? ? return Scaffold(

? ? ? appBar: AppBar(

? ? ? ? title: Text("錯誤頁面"),

? ? ? ),

? ? ? body: Container(

? ? ? ? child: Center(

? ? ? ? ? child: Text("頁面跳轉(zhuǎn)錯誤"),

? ? ? ? ),

? ? ? ),

? ? );

? }

}

并且設(shè)置onUnknownRoute

onUnknownRoute: (settings) {

? return MaterialPageRoute(

? ? builder: (ctx) {

? ? ? return UnknownPage();

? ? }

? );

},

微信掃一掃

關(guān)注該公眾號

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茉盏,一起剝皮案震驚了整個濱河市鉴未,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸠姨,老刑警劉巖铜秆,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異讶迁,居然都是意外死亡连茧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門巍糯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啸驯,“玉大人,你說我怎么就攤上這事祟峦》6罚” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵宅楞,是天一觀的道長针姿。 經(jīng)常有香客問我袱吆,道長,這世上最難降的妖魔是什么距淫? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任绞绒,我火速辦了婚禮,結(jié)果婚禮上榕暇,老公的妹妹穿的比我還像新娘蓬衡。我一直安慰自己,他們只是感情好彤枢,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布狰晚。 她就那樣靜靜地躺著,像睡著了一般堂污。 火紅的嫁衣襯著肌膚如雪家肯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天盟猖,我揣著相機與錄音讨衣,去河邊找鬼。 笑死式镐,一個胖子當著我的面吹牛反镇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娘汞,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼歹茶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了你弦?” 一聲冷哼從身側(cè)響起惊豺,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎禽作,沒想到半個月后尸昧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡旷偿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年烹俗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萍程。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡幢妄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茫负,到底是詐尸還是另有隱情蕉鸳,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布忍法,位于F島的核電站潮尝,受9級特大地震影響无虚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衍锚,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗤堰。 院中可真熱鬧戴质,春花似錦、人聲如沸踢匣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽离唬。三九已至后专,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間输莺,已是汗流浹背戚哎。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嫂用,地道東北人型凳。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像嘱函,于是被迫代替她去往敵國和親甘畅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

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