Wait for Navigator.pop ignoring Navigator.pushReplacement

Viewed 1917

I have the following setup:

class FirstScreen {
  // ...
  Future<void> doSomething() async {
    final bool isCool = await Navigator.of(context).pushNamed('/second-screen');
    print(isCool ? 'Cool.' : 'Not cool.');
  }
  // ...
}

class SecondScreen {
  // ...
  Future<void> replace() async {
    await Navigator.of(context).pushReplacementNamed('/third-screen');
  }
  // ...
}

class ThirdScreen {
  // ...
  Future<void> goBack() async {
    await Navigator.of(context).pop(true);
  }
  // ...
}

However, this would crash, since the pushReplacement procs the await and my application won't wait until the pop is used.

How can I wait for pop 's value to be returned?

UPDATE:

The problem here is a little bit more complex than I told.

@Alok suggested to not pop the route but push it after the sequence, however, this is a very trivial version of my code.

I currently have a HomeScreen with a nested Navigator that pushes to a list of questions. Then, using Navigator.of(context, rootNavigator: true), I navigate to the examLoadingScreen, etc. (You can read about this in the comments)

If I push the HomeScreen when the exam is completed, I would lose all the navigation done in the mentioned nested Navigator.

I seriously need to pop in this scenario. I have multiple workarounds such as pop chaining but it doesn't seem very performant or convenient.

1 Answers

See, Zeswen, as far this documentation on pushReplacementNamed is concerned. It states that:

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.

Can you see that, it clearly mentions that it removes the previous route after you are done animating it.

Now, what are you trying to achieve is, or how Navigator.pop() value retrieval works, is it is mandatory to have that PrevoiusPage there when you move from one page to another

//What you're doing with pushReplacementNamed
1 -> SeconPage => ThidPage
2 -> SecondPage [Removed] 
3 -> ThirdPage is trying to come to the previous page, that is SecondPage to return it's value, but SecondPage has been removed HENCE CRASHES!!

//What is needs to be done to use something like push() or pushNamed(), which used named route
1 -> SecondPage => ThirdPage
2 -> SecondPage is there in the stack
3 -> ThirdPage => SecondPage [Returns Value]

REMEMBER pop() always need the immediate precedence to accept it's value, not any page. So, if you remove the SecondPage, it will always crash.

Now, if you want to go to the page MainPage or in this case, FirstPage. Use pushAndRemoveUntil. It basically removes all the routes in the stack, and go to the page

SOLUTION: Pass the result score to the MainPage, via ResultPage. Make the MainPage accepts the Result Score too

class ThirdScreen(){
  // ...
  Future<void> goBack() async {
    await Navigator.pushAndRemoveUntil(context, 
        MaterialPageRoute( builder: (context) => FirstPage(result: result),
        (_) => false
    );
  }
}

And do your operation in your FirstPage accordingly, if you have result != 0 || result != null, and show it to the user. Let me know if that works out for you.

UPDATED ANSWER WITH A BEST POSSIBLE WORKAROUND

I have just added this answer, because, I feel like the above would be helpful in future as well.

Now, my idea is basic, and is workable according to the trivial information available for me.

THEORY: According to the theory, pop() value can be accessed by the predecessor only, immediate one.

SOLUTION

1. First Page -> Second Page
2. Second Page -> Third Page
3. Third Page -> Second Page with value

// Now following 3. step
1. Value check, if the value is true, pop immediately
2. Return the value to the first page
3. Print the value in the first page

Just follow your trivial data, and I hope you would understand the logic. After that implementation is just a cakewalk.

class FirstScreen {
  
  Future<void> doSomething() async {
    // We get the value from second page, which is technically passing 
    // the third page's value, and doesn't appear to us in UI
    // So serving the purpose
    final bool isCool = await Navigator.pushNamed(context, '/second-screen');
    print(isCool ? 'Cool.' : 'Not cool.');
  }
}

class SecondScreen {

  Future<void> replace() async {
    // No need of pushReplacementNamed, since we're are popping 
    // based upon our values, so it won't appear eventually
    // and pass the value as well for the First Page
    final bool value = await Navigator.pushNamed(context, '/third-screen');

    // Now we check, whether what value we got from third page,
    // If that is true, then immediately pop and return the value for first page
    if(value == true){
        Navigator.pop(context, value);
    }
  }
}

class ThirdScreen {
  // async not required for performing pop()
  // void is fine
  void goBack() {
    Navigator.pop(context, true);
  }
}

Just check it. This logic will help you achieve the purpose, and it is safe and error free.

Related