Flutter Navigator 高級用法


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM