“This is the 31st day of my participation in the August Gwen Challenge, for more details: August Gwen Challenge” juejin.cn/post/698796…

Xiao CAI learned the basic usage of page jump route when he first got into Flutter last year. With the gradual learning and application of scenarios, he tried to use routes in some special scenarios.

Pop / Push

pushReplacementNamed / pushReplacement

PushReplacementNamed/pushReplacement replaces the old page with the new one, and the old page is destroyed.

For example, if A -> B -> C uses pushReplacementNamed/pushReplacement on A page, B will be replaced by C, and C will return the page directly to A, where B will be destroyed during replacement. Usage scenarios can be used for SplashActivity -> MainActivity;

@optionalTypeArgs Future<T> pushReplacement <T extends Object, TO extends Object>( BuildContext context, Route<T> newRoute, {TO result}Copy the code
FlatButton(
    color: Colors.green,
    child: btnText('PushReplacementNamed -> ForthPage'),
    onPressed: () {
      Navigator.pushReplacementNamed(context, 'forthPage',
          result: '{result: pushReplacementNamed}');
    }),
FlatButton(
    color: Colors.greenAccent,
    child: btnText('PushReplacement -> ForthPage'),
    onPressed: () {
      Navigator.pushReplacement(context,
          MaterialPageRoute(builder: (BuildContext context) {
        return ForthPage();
      }), result: '{result: pushReplacement}');
    }),
Copy the code

popAndPushNamed

PopAndPushNamed can destroy this page and open a new page, the old page will be destroyed;

For example, when A -> B -> C uses popAndPushNamed to C on B page, B will destroy it first and then open C;

@optionalTypeArgs Future<T> pushReplacement <T extends Object, TO extends Object>( BuildContext context, Route<T> newRoute, {TO result, Object arguments}Copy the code
FlatButton(
    color: Colors.redAccent,
    child: btnText('PopAndPushNamed -> ForthPage'),
    onPressed: () {
      Navigator.popAndPushNamed(context, 'forthPage',
          result: '{result: popAndPushNamed}');
    }),
Copy the code


A comparison:

In order to distinguish the tests, on SecondPage -> ThirdPage set the left in, left out routing dynamic effect; ForthPage -> set right-in, right-out routing activity at ForthPage

ForthPage is not displayed if pushReplacementNamed/pushReplacement is used to open ForthPage. PopAndPushNamed will animate ThirdPage exit;


pushNamedAndRemoveUntil / pushAndRemoveUntil

If using pushNamedAndRemoveUntil/pushAndRemoveUntil, predicate to false to display the page, and destroyed all the other pages in the stack; If it is true, it is the same as opening a new page.

If A -> B -> C uses pushNamedAndRemoveUntil/pushAndRemoveUntil to start the new page D on page C, then D will be started first and all C -> B -> A will be destroyed.

For example, A -> B -> C -> D uses pushNamedAndRemoveUntil/pushAndRemoveUntil to reach B, D -> C -> B -> A will be destroyed;

@optionaltyPeargs Future < T > pushNamedAndRemoveUntil < T extends Object > (BuildContext context, // Context String newRouteName, // prejump page RoutePredicate, {// page status Object arguments})Copy the code
FlatButton(
    color: Colors.teal,
    child: btnText('PushAndRemoveUntil -> SecondPage/false'),
    onPressed: () {
      Navigator.pushAndRemoveUntil(context,
          MaterialPageRoute(builder: (BuildContext context) {
        return SecondPage();
      }), (route) => false);
    }),
FlatButton(
    color: Colors.pinkAccent,
    child: btnText('PushAndRemoveUntil -> SecondPage/true'),
    onPressed: () {
      Navigator.pushNamedAndRemoveUntil(context, 'secondPage', (route) => true);
    }),
Copy the code

popUntil

When popUntil is used, if the pre-arrived page is already in the page stack, it destroys the upper stack pages of the pre-arrived page one by one, starting from itself. Much like the In-stack reuse pattern of Android SingleTask; If the pre-arrived page is not in the page stack, the method cannot be used, and the ordinary Push method can be used.

If A -> B -> C -> D reaches A in popUntil mode, D -> C -> B is destroyed and reaches A in sequence.

Void popUntil (BuildContext context, // RoutePredicate // pre-jump page)Copy the code

Modalroute. withName(‘firstPage’) is not available for firsPage.

// ่ทณ่ฝฌ SecondPage
FlatButton(
    color: Colors.teal,
    child: btnText('PopUntil To -> SecondPage'),
    onPressed: () {
      Navigator.popUntil(context, ModalRoute.withName('secondPage'));
    }),
    
// ่ทณ่ฝฌ้ฆ–้กต FirstPage
FlatButton(
    color: Colors.orange,
    child: btnText('PopUntil To -> FirstPage'),
    onPressed: () {
      Navigator.popUntil(context, ModalRoute.withName(Navigator.defaultRouteName));
//    Navigator.popUntil(context, (route) => route.isFirst);
//    Navigator.popUntil(context, ModalRoute.withName('/'));
    }),
Copy the code


Compared to 2:

The difference between the two methods is that the pushNamedAndRemoveUntil/pushAndRemoveUntil method starts a new page and clears the original stack regardless of whether the stack is present or not. PopUntil simply backs up to the existing page on the stack and loops through pop() until the page is displayed;


Imitation popUntil refs

The popUntil method does not support the result content callback, but there are many situations in daily development where result needs to be passed; Side dishes are tried in the following two ways;

Try one:

PopUntil source code, is indeed in the loop to execute the pop() method, and no result method, then verify that each page received null;

void popUntil(RoutePredicate predicate) { while (! predicate(_history.last)) pop(); }Copy the code

In this way, we can use the most direct method to manually call POP (result) to pass a status code, receive judgment in the then method of each page, and then call POP (result) until the target page; Xiao CAI believes that the advantages of this approach are simple implementation and clear thinking; The disadvantage is that each page needs to receive a callback judgment;

Flutter_popuntil, defined by Nguyentuanhung, is also implemented in the same way;

// define a Result class PopWithResults<T> {// poped from this page final String fromPage; /// pop until this page final String toPage; /// results final Map<String, T> results; /// constructor PopWithResults({@required this.fromPage, @required this.toPage, this.results}); Class Page4 extends StatelessWidget {@override Widget build(BuildContext context) {return Scaffold(body: Center( child: RaisedButton( child: Text("back to page 2"), onPressed: () { Navigator.of(context).pop(PopWithResults( fromPage: 'page4', toPage: 'page2', results: {"pop_result": "this is the pop's result"})); }))); } } class Page3 extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: RaisedButton( child: Text("to page 4"), onPressed: () { Navigator.of(context).pushNamed('page4').then((results) { if (results is PopWithResults) { PopWithResults popResult  = results; if (popResult.toPage == 'page3') { // TODO do stuff } else { Navigator.of(context).pop(results); }}}); }))); } } class Page2 extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: RaisedButton( child: Text("to page 3"), onPressed: () { Navigator.of(context).pushNamed('page3').then((results) { if (results is PopWithResults) { PopWithResults popResult  = results; If (popResult toPage = = 'page2) {print (' the result is - > ${popResult. Results. Values. ToList () [0]}'); }}}); }))); }}Copy the code

Try 2:

The above method is simple and effective, but may be slightly complicated for very complex processes. Small dish take an extreme example, continuously start 10 pages, if using the above method, then judgment processing needs to be carried out on 10 pages;

The second approach is to use the state management Provider to assign a value to a Provider when popUntil is called and get the content of the Provider when the destination page is reached. Basic usage of the Provider that you can refer to and organize as needed;

class PopResult with ChangeNotifier { String _from = '', _result = ''; String get from => _from; String get result => _result; void setFrom(from) { _from = from; notifyListeners(); } void setResult(res) { _result = res; notifyListeners(); } } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider(builder: (_) => PopResult()) ], child: Consumer<PopResult>(builder: (context, counter, _) { return MaterialApp( home: FirstPage(), routes: { 'firstPage': (BuildContext context) => FirstPage(), 'secondPage': (BuildContext context) => SecondPage(), 'thirdPage': (BuildContext context) => ThirdPage(), 'forthPage': (BuildContext context) => ForthPage() }); })); } } class FirstPage extends StatefulWidget { @override _FirstPageState createState() => new _FirstPageState(); } class _FirstPageState extends State<FirstPage> { @override Widget build(BuildContext context) { return Scaffold( body:  Center( child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[ FlatButton( color: Colors.pinkAccent, child: btnText('Push To -> SecondPage'), onPressed: () { Navigator.pushNamed(context, "secondPage").then((val) { print('FirstPage-->$val'); if (Provider.of<PopResult>(context).from == 'forthPage') { print('FirstPage.from---->${Provider.of<PopResult>(context).from}'); Print (' firstpage.result ---->${Provider. Of <PopResult>(context).result}'); Provider.of<PopResult>(context).setFrom(" ");}});})))}} class ForthPage extends StatefulWidget { @override _ForthPageState createState() => new _ForthPageState(); } class _ForthPageState extends State<ForthPage> { @override Widget build(BuildContext context) { return Scaffold( body:  Center( child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[ FlatButton( color: Colors.blueAccent, child: btnText('PopUntil Provider -> FirstPage With Result'), onPressed: () { Provider.of<PopResult>(context, listen: false)..setFrom('forthPage')..setResult('I am from ForthPage'); Navigator.popUntil(context, ModalRoute.withName('/')); })]))); }}Copy the code

Routing dynamic effect

The small dish implements a simple dialog box: pop up from the bottom and transparency from 0.0 to 1.0; But small dishes in the test barrierColor entered in the dialog box display after the completion of the display, in the exit gradually disappear; Still to be studied;

FlatButton( color: Colors.green, child: Text('ShowDialog'), onPressed: () { Navigator.of(context).push(PageRouteBuilder( opaque: false, barrierDismissible: false, barrierColor: Colors. Black12. WithOpacity (0.5), maintainState: false, transitionDuration: const Duration (milliseconds: 1000), transitionsBuilder: (context, animation, secondaryAnimation, child) { return SlideTransition( position: Tween<Offset>(begin: Offset(0.0, 1.0), end: Offset(0.0, 0.0)). Animate (CurvedAnimation(parent: animation, curve: Curves. FastOutSlowIn), child: FadeTransition(opacity: Tween(begin: 0.0, end: 1.0). Animate (CurvedAnimation(parent: animation, curve: Curves.linear)), child: child)); }, pageBuilder: (context, animation, secondaryAnimation) {return Center(child: Container(height: 200.0, width: 200.0, decoration: BoxDecoration(color: Colors. GreenAccent, borderRadius: borderRadius. Circular (5.0)), child: Icon(Icons.ac_unit, color: Colors.white))); })); }),Copy the code


Small dish to the page jump routing bottom learning is not deep enough, if there are mistakes, please guide! The design code in the blog is the core code, only for reference!

Source: Little Monk A Ce