路由管理
路由(Route)在移動(dòng)開發(fā)中通常指頁(yè)面(Page)幸逆,這跟web開發(fā)中單頁(yè)應(yīng)用的Route概念意義是相同的,Route在Android中通常指一個(gè)Activity,在iOS中指一個(gè)ViewController漫雕。
所謂路由管理,就是管理頁(yè)面之間如何跳轉(zhuǎn)峰鄙,通常也可被稱為導(dǎo)航管理浸间。
Flutter中的路由管理和原生開發(fā)類似,無(wú)論是Android還是iOS吟榴,導(dǎo)航管理都會(huì)維護(hù)一個(gè)路由棧魁蒜,路由入棧(push)操作對(duì)應(yīng)打開一個(gè)新頁(yè)面,路由出棧(pop)操作對(duì)應(yīng)頁(yè)面關(guān)閉操作,而路由管理主要是指如何來(lái)管理路由棧兜看。
Flutter路由的詳細(xì)使用
(一)Navigator
在Android中锥咸,我們開啟新的頁(yè)面是Activity。在iOS中细移,我們開啟新的頁(yè)面是ViewControllers搏予。在Flutter中,每一個(gè)頁(yè)面都是小部件弧轧, 我們?nèi)绾伍_啟到新的頁(yè)面呢雪侥?
Flutter給我們提供了一個(gè)API,叫做Navigator
Navigator 繼承自 StatefulWidget,它也是小組件精绎,它有很多相關(guān)靜態(tài)函數(shù)速缨,可以幫我們達(dá)到頁(yè)面跳轉(zhuǎn)和數(shù)據(jù)交互的功能:
函數(shù) | 作用 |
---|---|
push | 將設(shè)置的router信息推送到Navigator上,實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)代乃。 |
of | 主要是獲取 Navigator最近實(shí)例的好狀態(tài)鸟廓。 |
pop | 導(dǎo)航到新頁(yè)面,或者返回到上個(gè)頁(yè)面襟己。 |
canPop | 判斷是否可以導(dǎo)航到新頁(yè)面 |
maybePop | 可能會(huì)導(dǎo)航到新頁(yè)面 |
popAndPushNamed | 指定一個(gè)路由路徑引谜,并導(dǎo)航到新頁(yè)面。 |
popUntil | 反復(fù)執(zhí)行pop 直到該函數(shù)的參數(shù)predicate返回true為止擎浴。 |
pushAndRemoveUntil | 將給定路由推送到Navigator员咽,刪除先前的路由,直到該函數(shù)的參數(shù)predicate返回true為止贮预。 |
pushNamed | 將命名路由推送到Navigator贝室。 |
pushNamedAndRemoveUntil | 將命名路由推送到Navigator,刪除先前的路由仿吞,直到該函數(shù)的參數(shù)predicate返回true為止滑频。 |
pushReplacement | 路由替換。 |
pushReplacementNamed | 這個(gè)也是替換路由操作唤冈。推送一個(gè)命名路由到Navigator峡迷,新路由完成動(dòng)畫之后處理上一個(gè)路由。 |
removeRoute | 從Navigator中刪除路由你虹,同時(shí)執(zhí)行Route.dispose操作绘搞。 |
removeRouteBelow | 從Navigator中刪除路由,同時(shí)執(zhí)行Route.dispose操作傅物,要替換的路由是傳入?yún)?shù)anchorRouter里面的路由夯辖。 |
replace | 將Navigator中的路由替換成一個(gè)新路由。 |
replaceRouteBelow | 將Navigator中的路由替換成一個(gè)新路由董饰,要替換的路由是是傳入?yún)?shù)anchorRouter里面的路由蒿褂。 |
(二)路由的操作方式
(1)使用Navigator.push實(shí)現(xiàn)發(fā)送路由圆米,Navigator.pop返回上一個(gè)頁(yè)面。
@optionalTypeArgs
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
push函數(shù)的參數(shù)1是 上下文啄栓,參數(shù)2是 Router榨咐,我們這里使用的是 Router的孫子類(好幾層繼承的子類)MaterialPageRouter 這個(gè)類。
該類必須要傳入一個(gè)閉包函數(shù) WidgetBuilder谴供,該閉包函數(shù)的參數(shù)是 BuildContext對(duì)象块茁,我們這里使用的是匿名函數(shù)的形式,加上胖箭頭符號(hào)桂肌,簡(jiǎn)寫成這樣:builder: (context) => new App(); 相信大家看到這里也基本能看明白這句表達(dá)式的含義数焊。
返回上一個(gè)頁(yè)面使用
Navigator.pop(context);
頁(yè)面A 的代碼如下圖所示:
第一點(diǎn):push使用
1.pushNamed
Navigator.of(context).pushNamed('routeName');
此種方法只是簡(jiǎn)單的將我們需要進(jìn)入的頁(yè)面push到棧頂,以此來(lái)顯示當(dāng)前頁(yè)面,其參數(shù)是一個(gè)字符串類型崎场,傳入的是頁(yè)面對(duì)應(yīng)的路由名稱
該路由名稱需要在程序主入口中進(jìn)行定義佩耳。定義方法為:
void main() {
runApp(
new MaterialApp(
home: new Screen1(),
routes: <String, WidgetBuilder> {
'/screen1': (BuildContext context) => new Screen1(),
'/screen2' : (BuildContext context) => new Screen2(),
'/screen3' : (BuildContext context) => new Screen3(),
},
)
);
}
使用:Navigator.of(context).pushNamed('/screen1'); 直接進(jìn)入screen1頁(yè)面(每次都將新建一個(gè)新的頁(yè)面)
2.1 pushReplacementNamed
Navigator.of(context).pushReplacementNamed('/screen4');
指把當(dāng)前頁(yè)面在棧中的位置替換成跳轉(zhuǎn)的頁(yè)面(替換導(dǎo)航器的當(dāng)前路由,通過(guò)推送路由[routeName])谭跨,當(dāng)新的頁(yè)面進(jìn)入后干厚,之前的頁(yè)面將執(zhí)行dispose方法。
下面為官方說(shuō)明:
Replace the current route of the navigator that most tightly encloses the
given context by pushing the route named [routeName] and then disposing
the previous route once the new route has finished animating in.
@optionalTypeArgs
Future<T> pushReplacementNamed<T extends Object, TO extends Object>(
String routeName, {
TO result,
Object arguments,
}) {
return pushReplacement<T, TO>(_routeNamed<T>(routeName, arguments: arguments), result: result);
}
即比如當(dāng)前從頁(yè)面1進(jìn)入頁(yè)面2螃宙,在頁(yè)面2使用
Navigator.of(context).pushReplacementNamed('/screen3');進(jìn)入頁(yè)面3蛮瞄,當(dāng)進(jìn)入了頁(yè)面3后,頁(yè)面2將執(zhí)行dispose方法谆扎,此時(shí)在頁(yè)面3返回時(shí)挂捅,會(huì)回到頁(yè)面1.
使用情況:例如
從SplashScreen到HomeScreen。它應(yīng)該只顯示一次堂湖,用戶不應(yīng)該再?gòu)闹髌聊换氐剿邢取T谶@種情況下,由于我們將要進(jìn)入一個(gè)全新的屏幕无蜂,
我們可能想要使用這個(gè)方法來(lái)實(shí)現(xiàn)它的enter animation屬性伺糠。
2.2 pushReplacement
Navigator.pushReplacement( context, MaterialPageRoute(builder: (BuildContext context) => screen4()));
這個(gè)用法跟2.1相同,只是路由的傳遞有差別斥季,上方的是傳遞路由名稱(頁(yè)面對(duì)應(yīng)的名稱训桶,需在入口定義(本文第一點(diǎn))),而后者只需new對(duì)應(yīng)頁(yè)面即可泻肯,而且可以傳遞
參數(shù)(傳參方式類似于本文后續(xù)所說(shuō)的傳遞方法)渊迁。
默認(rèn)使用MaterialPageRoute
3.popAndPushNamed
Navigator.popAndPushNamed(context, '/screen4');
指將當(dāng)前頁(yè)面pop,然后跳轉(zhuǎn)到制定頁(yè)面(將當(dāng)前路由彈出導(dǎo)航器灶挟,并將命名路由推到它的位置。)
下面為官方說(shuō)明:
Pop the current route off the navigator that most tightly encloses the
given context and push a named route in its place.
使用情況:例如
在購(gòu)物應(yīng)用中毒租,有產(chǎn)品列表稚铣,用戶在產(chǎn)品列表中可以通過(guò)篩選箱叁,來(lái)進(jìn)一步選擇商品,在這個(gè)過(guò)程中惕医,用戶點(diǎn)擊篩選按鈕時(shí)耕漱,會(huì)進(jìn)入篩選條件選擇界面,當(dāng)用戶點(diǎn)擊
確定篩選按鈕時(shí)抬伺,應(yīng)彈出篩選界面螟够,并使用新的篩選條件進(jìn)入產(chǎn)品列表。這種情況popAndPushNamed就更合適了峡钓。
4.pushNamedAndRemoveUntil
Navigator.of(context).pushNamedAndRemoveUntil('/screen4', (Route<dynamic> route) => false);
指將制定的頁(yè)面加入到路由中妓笙,然后將其他所有的頁(yè)面全部pop, (Route<dynamic> route) => false將確保刪除推送路線之前的所有路線。
這時(shí)候?qū)⒋蜷_一個(gè)新的screen4頁(yè)面
Push the route with the given name onto the navigator, and then remove all
the previous routes until the `predicate` returns true.
使用情況:例如
當(dāng)用戶點(diǎn)擊了退出登錄時(shí)能岩,我們需要進(jìn)入某一個(gè)頁(yè)面(比如點(diǎn)退出登錄后進(jìn)入了登錄頁(yè))寞宫,這個(gè)時(shí)候用戶點(diǎn)擊返回時(shí)不應(yīng)該能進(jìn)入任何一個(gè)頁(yè)面,這種情況就可以使用拉鹃。
5.1 pushNamedAndRemoveUntil
Navigator.of(context).pushNamedAndRemoveUntil('/screen4', ModalRoute.withName('/screen1'));
指將制定的頁(yè)面加入到路由中辈赋,然后將之前的路徑移除知道制定的頁(yè)面為止(將具有給定名稱的路由推到導(dǎo)航器上,然后刪除所有路徑前面的路由直到'predicate'返回true為止膏燕。)
這時(shí)候?qū)N毀棧內(nèi)除了screen4的頁(yè)面钥屈,點(diǎn)擊直接去棧內(nèi)screen4,這時(shí)screen4會(huì)重新build
Push the route with the given name onto the navigator, and then remove all
the previous routes until the `predicate` returns true.
使用情況:例如
一個(gè)購(gòu)物應(yīng)用程序的例子!或者任何需要支付交易的應(yīng)用程序坝辫。因此焕蹄,在這些應(yīng)用程序中,一旦用戶完成了支付事件阀溶,所有與交易或購(gòu)物車相關(guān)的屏幕都應(yīng)該從堆棧中刪除腻脏,用戶應(yīng)該進(jìn)入到支付確認(rèn)頁(yè)面。單擊back按鈕應(yīng)將它們返回到產(chǎn)品列表或主屏幕银锻。
使用實(shí)例:
1-->2-->3,3到4時(shí)使用Navigator.pushNamedAndRemoveUntil(context,"/screen4",ModalRoute.withName('/screen1'));
這時(shí)候如果在頁(yè)面4點(diǎn)擊返回永品,將會(huì)直接退出程序。
1-->2-->3,3到4時(shí)使用Navigator.pushNamedAndRemoveUntil(context,"/screen4",ModalRoute.withName('/'));
這時(shí)候如果在頁(yè)面4點(diǎn)擊返回击纬,將會(huì)直接回到頁(yè)面1鼎姐。
1-->2-->1-->2-->3,3到4時(shí)使用Navigator.pushNamedAndRemoveUntil(context,"/screen4",ModalRoute.withName('/screen1'));
這時(shí)候如果在頁(yè)面4點(diǎn)擊返回,將會(huì)回到第二個(gè)1頁(yè)面更振。
5.2 pushAndRemoveUntil
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (BuildContext context) => new screen4()),
ModalRoute.withName('/'),
這種方法跟上述方法作用相同炕桨,不同之處在于,上述傳遞的是路由名稱肯腕,這個(gè)名稱需要你在入口處進(jìn)行路由指定献宫,而這種則無(wú)需指定,直接new 出來(lái)即可实撒,
而且可以傳遞參數(shù)姊途。(看其名稱即可發(fā)現(xiàn)差別pushNamedAndRemoveUntil與pushAndRemoveUntil)使用這種作用如下
1-->2-->3,3到4時(shí)使用此方法涉瘾,這時(shí)候如果在頁(yè)面4點(diǎn)擊返回,將會(huì)直接回到頁(yè)面1捷兰。
如果使用
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (BuildContext context) => Screen4()),
(Route<dynamic> route) => false,
);
這時(shí)候進(jìn)入4后立叛。4將成為唯一的一個(gè)頁(yè)面。其他頁(yè)面都將pop出棧贡茅,這個(gè)跟上述pushNamedAndRemoveUntil也一致秘蛇。
6.popUntil
Navigator.popUntil(context, ModalRoute.withName('/screen2'));
有些應(yīng)用場(chǎng)景下,用戶可能不得不填寫一個(gè)由三部分組成的長(zhǎng)表單顶考,該表單可能在移動(dòng)應(yīng)用程序的三個(gè)連續(xù)屏幕中顯示×藁梗現(xiàn)在在表單的第三個(gè)頁(yè)面,用戶決定取消填寫表單村怪。用戶單擊Cancel秽浇,就會(huì)彈出所有之前的與表單相關(guān)的屏幕,并將用戶帶回主屏幕甚负,從而丟失所有與表單相關(guān)的數(shù)據(jù)(在這種情況下柬焕,這是我們想要的)。我們不會(huì)在這里推出任何新東西梭域,只是回到以前的路線斑举。
pop
1.Navigator.of(context).maybePop();
maybePop 會(huì)自動(dòng)進(jìn)行判斷,如果當(dāng)前頁(yè)面pop后病涨,會(huì)顯示其他頁(yè)面富玷,不會(huì)出現(xiàn)問(wèn)題,則將執(zhí)行當(dāng)前頁(yè)面的pop操作
否則將不執(zhí)行既穆。
2.Navigator.of(context).canPop();
canPop 判斷當(dāng)前頁(yè)面能否進(jìn)行pop操作缅疟,并返回bool值
3.Navigator.of(context).pop();
直接退出當(dāng)前頁(yè)面
傳參和參數(shù)返回
傳參的方式很簡(jiǎn)單拌倍,在需要接收參數(shù)的頁(yè)面進(jìn)行參數(shù)定義,并加入其構(gòu)造函數(shù)中,在跳轉(zhuǎn)到該頁(yè)面時(shí)荚虚,使用MaterialPageRoute并在頁(yè)面中傳入?yún)?shù)即可添怔。
String params;
Navigator.push(context, new MaterialPageRoute(builder: (BuildContext context) => new mainPage(params)));
接收參數(shù)
class mainPage extends StatelessWidget {
final String userName;
mainPage(this.userName);
@override
Widget build(BuildContext context) {
print(userName);
}
帶返回值的頁(yè)面跳轉(zhuǎn):
String userName = "yinll";
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new Screen5(userName))).then((data){
result =data;
print(result);
});
然后screen5中续徽,在返回時(shí)使用:Navigator.of(context).pop('這是頁(yè)面5返回的參數(shù)');
在pop中寫上返回的的值畏妖,這時(shí)候在上方的then中即可得到返回的數(shù)據(jù)。