https://blog.csdn.net/u013894711/article/details/100729879
在上一篇我們已經講了Flutter Navigation的基本使用,有興趣或者對這一塊還不太了解的同學可以去看看。在實際項目中我們可能會遇到一些比較特殊或者復雜的需求,而基本的跳轉方式已經不能滿足了,這一篇我們主要看一下Flutter的一些比較品如的跳轉操作。
不得不說Flutter真的很良心,提供的一些跳轉方式都非常實用,讓我們日常的開發效率提升了很多,下面我們就一個一個的來介紹吧。
1.PushAndRemove
假設有這么一個場景:我們在開發一個商城項目,從選擇商品到支付完成會經過主頁面,商品列表頁,商品詳情頁,支付頁面,支付結果頁。這時候來了一個需求,當跳轉到支付結果頁的時候將之前所有的頁面都銷毀,只留下主頁面。如果是android可能會用到singletask或者自己寫一個activity管理器之類的,但是這些實現方式都顯得不那么優雅直接,而Flutter給出的跳轉方案能完美地解決這類問題。
下面我們來看一下對於這類場景Flutter地解決方案
新建4個頁面,Screen1,Screen2,Screen3,Screen4
在MaterialApp中添加這4個頁面的路由
使用Navigator.of(context).pushAndRemoveUntil()或者Navigator.of(context).pushNamedAndRemoveUntil()進行跳轉
1.1 新建頁面
class Screen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
Navigator.pushNamed(context, '/screen2');
},
child: Text(
'screen1',
style: TextStyle(fontSize: 30),
),
),
);
}
}
其它三個頁面的樣式和Screen1保持一致,點擊按鈕跳轉到下一個頁面
1.2 在MaterialApp中添加這4個頁面的路由
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/screen1': (context) => Screen1(),
'/screen2': (context) => Screen2(),
'/screen3': (context) => Screen3(),
'/screen4': (context) => Screen4(),
},
initialRoute: '/screen1',
);
}
1.3 進行跳轉操作
本例中我們的跳轉順序是Screen1—>Screen2—>Screen3—>Screen4
當從Screen3跳轉到Screen4的時候我們希望將Screen2,Screen3從棧里面移除掉,這樣在Screen4點擊返回就能直接回到Screen1
使用Navigator.pushAndRemoveUtil()或者Navigator.pushNamedAndRemoveUntil()實現,這個方法有兩個必傳參數newRoute和predicate,第一個參數表示將要加入棧中的頁面,第二個參數表示棧中要保留的頁面底線,意思就是在predicate和newRoute之間的頁面都會被移除棧
class Screen3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
//跳轉到screen4,並且移除所有的頁面直到screen1
// Navigator.of(context).pushNamedAndRemoveUntil(
// '/screen4', ModalRoute.withName('/screen1'));
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => Screen4()),
ModalRoute.withName('/screen1'));
},
child: Text(
'screen3',
style: TextStyle(fontSize: 30),
),
),
);
}
}
2.pushReplacement
假設我們在做一個登錄功能,在登錄成功后需要跳轉到一個新的頁面並且銷毀當前登錄頁,這時候就可以用pushReplacement來實現,從字面上的意思看push很好理解,把一個新頁面壓入棧中嘛,replacement到底是替代那個頁面呢?答案是當前頁,因為被替換的對象是固定的,所以該方法的必傳參數只有被push的路由。
class Screen3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
//打開Screen4頁面,並銷毀當前頁
Navigator.of(context).pushReplacementNamed('/screen4');
//也可以使用以下方式
//Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=>Screen3()));
},
child: Text(
'screen3',
style: TextStyle(fontSize: 30),
),
),
);
}
}
3.popUntil
這個比較簡單,從字面意思上就可以看出是“一直退出直到某一個頁面”,來看一下用法吧
class Screen4 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
//當前在Screen4頁面,點擊回到Screen1,連帶着Screen2,Screen3也一起退出
Navigator.of(context).popUntil(ModalRoute.withName('/screen1'));
},
child: Text(
'screen4',
style: TextStyle(fontSize: 30),
),
),
);
}
}
4.popAndPushNamed
這個方法和pushReplacement很相近,都是開啟一個新的頁面並且銷毀之前的頁面,只是在邏輯上的執行順序不一樣,popAndPushNamed是退出當前頁面並且將新的頁面放到它原來的位置上,所以在視覺效果上是先退出再進入
class Screen2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
//點擊退出當前頁面,並將Screen3壓入棧中
Navigator.of(context).popAndPushNamed('/screen3');
},
child: Text(
'screen2',
style: TextStyle(fontSize: 30),
),
),
);
}
}
仔細看動圖,Screen2到Screen3的時候是先將Screen2退出再顯示Screen3,它不像pushReplacement那樣無感知的就把頁面給替換了。所以大家根據實際場景選擇使用。
備注:以上所有的方法都可以傳遞相應的參數,涉及到pop的可以加入返回參數,涉及到push的可以添加傳遞到新頁面的參數,這里沒有做過多的贅述,大家可以自己嘗試一下。
5.maybePop和canPop
Navigator.of(context).canPop()返回一個bool值,表示當前頁面是否可以退出,那么判斷的依據是啥呢?追溯到源碼去看
/// Whether the navigator can be popped.
///
/// {@macro flutter.widgets.navigator.canPop}
///
/// See also:
///
/// * [Route.isFirst], which returns true for routes for which [canPop]
/// returns false.
bool canPop() {
assert(_history.isNotEmpty);
return _history.length > 1 || _history[0].willHandlePopInternally;
}
尤其是這句話哦: [Route.isFirst], which returns true for routes for which [canPop],意思說的很明顯了,判斷依據就是看當前路由是否處在棧中“最底部”的位置。根據之前的例子,如果我們在Screen1調用canPop肯定返回false,因為它處在“最底部”的位置,而在其它頁面調用則返回true
Navigator.of(context).maybePop()是一種很友善的退出方式,如果能退出就退出,不退出拉到。其實這個方法可以理解為
maybePop() => canPop() == true?pop() : do nothing
所以在首頁調用maybePop()是不會退出的,如果在其它頁面調用,效果和pop()一樣
關於Navigator的push,pop使用姿勢就這么多了,不正確的地方歡迎大家指正!
————————————————
版權聲明:本文為CSDN博主「autonomousjack」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u013894711/java/article/details/100729879