Error thrown on navigator pop until : “!_debugLocked’: is not true.’

Instead of giving you a direct answer, I’m going to walk you through how I thought about this when I saw the question, in the hope that it’ll help you in the future.

The Assertion

The assertion says: Failed assertion: line 1995 pos 12: ‘!_debugLocked’: is not true. Hmm, interesting. Let’s take a look at that line of code.

assert(!_debugLocked);

Well, that doesn’t give me much more information, let’s look at the variable.

The Variable

The variable is:

bool _debugLocked = false; // used to prevent re-entrant calls to push, pop, and friends

That’s better. It’s there to prevent re-entrant calls to push, pop, etc (by that it means that it doesn’t want you calling ‘push’, ‘pop’, etc from within a call to ‘push’, ‘pop’). So let’s trace that back to your code.

The Likely Culprit

This seems like the likely culprit:

bottomSheet.closed.then((v) {
  Navigator.of(context)
    .popUntil((r) => r.settings.isInitialRoute);
});

I’m going to skip a step here and use deductive reasoning instead – I’m betting that the closed future is finished during a pop. Go ahead and confirm that by reading the code if you feel like it.

The Problem

So, if the issue is that we’re calling pop from within a pop function, we need to figure out a way to defer the call to pop until after the pop has completed.

Solution 1: Using Delayed Future

This becomes quite simple – there’s two ways to do this. The simple way is to just use a delayed future with zero delay, which will have dart schedule the call as soon as possible once the current call stack returns to the event loop:

Future.delayed(Duration.zero, () {
  Navigator. ...
});

Solution 2: Using Scheduler

The other more flutter-y way of doing it would be to use the Scheduler to schedule a call for after the current build/render cycle is done:

SchedulerBinding.instance.addPostFrameCallback((_) {
  Navigator. ...
});

Solution 3: Simplifying the Call

Another option is also possible though – in your ExtendedFloatingActionButton where you call pop:

ExtendedFloatingActionButton(
 text: "ORDER DETAILS",
  action: () {
    Navigator.of(context).pop();
  },
),

You could instead simply do the call to Navigator.of(context).popUntil…. That would eliminate the need for the doing anything after bottomSheet.closed is called. However, depending on whatever else you might or might not need to do in your logic this may not be ideal (I can definitely see the issue with having the bottom sheet set off a change to the main part of the page and why you’ve tried to make that happen in the page’s logic).

Additional Recommendations

Also, when you’re writing your code I’d highly recommend separating it into widgets – for example the bottom sheet should be its own widget. The more you have in a build function, the harder it is to follow and it can actually have an effect on performance as well.

You should also avoid using GlobalKey instances wherever possible – you can generally either pass objects (or callbacks) down if it’s only through a few layers, use the .of(context) pattern, or use inherited widgets.

Avoiding the Issue

For those who are invoking the Navigator as part of the build process. I found that it will intermittently throwing asserting error on the debugLocked.

I avoided this issue by wrapping with a addPostFrameCallback:

WidgetsBinding.instance.addPostFrameCallback((_) {
  Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => MyPage()));
});