前言
Flutter(本文用的flutter是1.9.1版本)和許多其他移動應(yīng)用一樣也是使用棧來管理頁面的敛摘,進入一個新頁面就是一個入棧操作门烂,而退出一個頁面時就是一個出棧操作。
在Flutter中一個頁面就是一個Route對象兄淫,而管理這里Route對象的就是Navigator,例如Navigator.push負(fù)責(zé)入棧Route對象慨丐,Navigator.pop負(fù)責(zé)出棧Route對象。
頁面跳轉(zhuǎn)相關(guān)API介紹
一泄私、跳轉(zhuǎn)到新頁面
相關(guān)方法
Future<T> push<T extends Object>(Route<T> route)
Future<T> pushNamed<T extends Object>(String routeName, {Object arguments, })
先看個例子(從Page1跳轉(zhuǎn)到Page2):
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page2()));
或:
Navigator.of(context).pushNamed("/page2");
首先房揭,對于Navigator的使用,從上面例子中可以看出是通過Navigator.of(context)
獲取到當(dāng)前的NavigatorState
對象(of
方法返回的是一個NavigatorState
對象)晌端,然后調(diào)用對應(yīng)的push
方法或pushNamed
方法。
這里也可以省略
of
方法蓬痒,直接將context參數(shù)寫到push
或pushNamed
方法中漆羔,如Navigator.pushNamed(context, "/page2");
本質(zhì)上是一樣的,來看看源碼:static Future<T> pushNamed<T extends Object>( BuildContext context, String routeName, { Object arguments, }) { return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments); }
可見演痒,最終也是先調(diào)用
Navigator.of(context)
方法來獲取到當(dāng)前的NavigatorState
對象,然后在調(diào)用對應(yīng)的方法鸟顺。
匿名路由
對于push
方法,前面我們說了Navigator管理的Route對象蹦锋,而一個頁面就是一個Route,而且通過上面的API也可以看出晕粪,push
的第一個參數(shù)是一個Route對象渐裸,因此這里我們需要將要跳轉(zhuǎn)的頁面包裝成一個Route對象傳給Navigator。這里我們用的是MaterialPageRoute
尚氛,一個帶有Material風(fēng)格的路由(比如實現(xiàn)了一些Material風(fēng)格的跳轉(zhuǎn)動畫效果等)洞渤。另外還有IOS風(fēng)格的路由CupertinoPageRoute
。如果想要實現(xiàn)自定義效果载迄,可以使用PageRouteBuilder去加自己想要的效果。
命名路由
pushNamed
方法我們只傳了一個字符串魂迄,這里又是這么生成Route對象的吶惋耙?這就是Flutter路由的另外一個使用方式了。要直接使用名字做跳轉(zhuǎn)需要我們先對路由進行命名:
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/page1': (context) => Page1(),
'/page2': (context) => Page2(),
'/page3': (context) => Page3(),
},
));
}
然后在頁面跳轉(zhuǎn)的時候我們只需要使用pushNamed
傳遞一個路由名字即可完成跳轉(zhuǎn)湿酸,系統(tǒng)會使用名字和對應(yīng)的路由構(gòu)造器自動為我們創(chuàng)建一個Route灭美。
注意:
- 對于
home
指定的頁面推溃,系統(tǒng)會自動命名為/
冲粤;- 這里的名字是可以重復(fù)的美莫,每個名字對應(yīng)的頁面也不是唯一的梯捕,所以這里是多對多的情況。比如home指定的參數(shù)是page1:
home: Page1()
;襟铭,下面又添加了一個page1:'/page1': (ontext) => Page1()
,當(dāng)程序啟動后(沒做跳轉(zhuǎn))寒砖,路由歷史棧里面Page1對應(yīng)的名字是/
而不是/page1
。
所以魁兼,這兩個方法的區(qū)別就是:使用push
進行頁面跳轉(zhuǎn)的時候我們不用提前進行命名漠嵌,但是每次跳轉(zhuǎn)的時候需要手動創(chuàng)建一個Route,而使用pushNamed
進行頁面跳轉(zhuǎn)的時候只需要一個名字即可儒鹿,但是需要提前命名。
另外植阴,按照上面例子中的寫法圾浅,push
跳轉(zhuǎn)時生成的Route是沒有名字的(是null),如需指定名字贱傀,可以這樣寫:
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Page2(),
settings: RouteSettings(name: "/page2"),
));
即在創(chuàng)建Route的時候除了指定builder
參數(shù)之外府寒,還可以傳遞一個settings
,在settings中去指定名字株搔。
參數(shù)傳遞
通過前面方法的簽名我們看到pushNamed
有兩個參數(shù),第二個是一個Object arguments
纵隔,這個就是用來傳遞參數(shù)的炮姨,類型是Object
,也就是說我們可以傳遞任意類型的參數(shù)舒岸。對于push
方法需要傳遞參數(shù)的話有兩種方式,第一種是在頁面的構(gòu)造函數(shù)中傳遞俄认,如:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => Page2(title: "hello")));
另外一種方式是通過前面提到過的settings
參數(shù)來傳遞,如:
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Page2(),
settings: RouteSettings(name: "/page2", arguments: {"title": "hello"}),
));
這里的arguments
同樣是一個Object
眯杏,也就是可以指定任意的參數(shù)類型。pushNamed
最終也是將名字和參數(shù)封裝到settings中的茫经。
在新頁面中萎津,通過以下方式取出傳遞的參數(shù):
@override
Widget build(BuildContext context) {
Object arguments = ModalRoute.of(context).settings.arguments;
// TODO
}
接收返回值
通過前面可以看到,push
和pushNamed
的返回值類型是一個Future<T>
姜性,這個就是前一個頁面的返回值髓考,如:
Navigator.of(context).pushNamed("/page2").then((value){
// 這里處理返回值
print("return value=$value");
})
至于上一個頁面如何設(shè)置返回值后面講解pop
方法的時候再說氨菇。
二、退出當(dāng)前頁面并跳轉(zhuǎn)到新頁面
Future<T> pushReplacement<T extends Object, TO extends Object>(Route<T> newRoute, { TO result })
Future<T> pushReplacementNamed<T extends Object, TO extends Object>(String routeName, {TO result, Object arguments, })
Future<T> popAndPushNamed<T extends Object, TO extends Object>(String routeName, {TO result, Object arguments, })
這三個方法的功能都是退出當(dāng)前頁面并進入新的頁面查蓉。1和2的區(qū)別就不說了,和前面一樣妹田。12和3的區(qū)別是:12兩個方法都是先進入新的頁面鹃共,然后再退出當(dāng)前頁面,也就是當(dāng)前頁面的退出動畫是看不見的霜浴;方法3是先退出當(dāng)前頁面,然后在進入新的頁面晌纫,也就是當(dāng)前頁面的退出動畫是看得見的永丝。
三、清除歷史頁面并跳轉(zhuǎn)到新頁面
Future<T> pushNamedAndRemoveUntil<T extends Object>(String newRouteName, RoutePredicate predicate, {Object arguments, })
Future<T> pushAndRemoveUntil<T extends Object>(Route<T> newRoute, RoutePredicate predicate)
這里重點是第二個參數(shù)predicate
类溢。在跳轉(zhuǎn)新頁面的時候會對當(dāng)前歷史棧里的頁面依次進行遍歷露懒,然后通過predicate
回調(diào)給用戶進行處理砂心,如果predicate
返回false就表示這個頁面需要退出辩诞,直到歷史遍歷完或者predicate
返回true為止坎弯。
比如:
// 這里第二個參數(shù)始終返回false译暂,則會清除所有歷史頁面,該方法執(zhí)行完成后只會存在page2一個頁面
Navigator.of(context).pushNamedAndRemoveUntil("/page2", (route) => false);
// 清除/page2之上的所有頁面
Navigator.of(context).pushNamedAndRemoveUntil("/page4", (route) {
return route.settings.name == "/page2";
});
// 清除除了根頁面之外的所有歷史頁面
Navigator.of(context).pushNamedAndRemoveUntil("/page2", ModalRoute.withName("/"));
這里的ModalRoute.withName
主要也是對名字進行判斷:
static RoutePredicate withName(String name) {
return (Route<dynamic> route) {
return !route.willHandlePopInternally
&& route is ModalRoute
&& route.settings.name == name; // 判斷名字是否是指定的名字
};
}
四崎脉、退出當(dāng)前頁面
-
bool pop<T extends Object>([ T result ])
:退出當(dāng)前頁面伯顶,result
為要返回的參數(shù); -
void popUntil(RoutePredicate predicate)
:退出歷史棧中的頁面灶体,直到predicate
返回true掐暮;
另外還有兩個方法:
-
bool canPop()
:檢查當(dāng)前頁面是否可以返回; -
Future<bool> maybePop<T extends Object>([ T result ]) async
:嘗試進行返回(不一定成功)樟结;
自定義路由
這里給個簡單的自定義實現(xiàn):
Navigator.of(context).push(
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) {
return FadeTransition(
opacity: animation,
child: Page2(),
);
},
),
);
具體請見官方文檔的Custom routes
部分精算。