# Navigator onPopPage versus onDidRemovePage

During the development of my [RubigoRouter](https://github.com/jsroest/rubigo_router) package I ran into some limitations of the `Navigator` object that I explain here below.

The Flutter Navigator has a mechanism to inform the app about a back navigation event.

Historically the `onPopPage` callback was used for this, but with the new [predictive-back gesture](https://docs.flutter.dev/platform-integration/android/predictive-back) or the iOS-style back swipe, a new `onDidRemovePage` callback is introduced. Below, we can compare the two signatures.

```dart
/// Signature for the [Navigator.onPopPage] callback.
///
/// This callback must call [Route.didPop] on the specified route and must
/// properly update the pages list the next time it is passed into
/// [Navigator.pages] so that it no longer includes the corresponding [Page].
/// (Otherwise, the page will be interpreted as a new page to show when the
/// [Navigator.pages] list is next updated.)
typedef PopPageCallback = bool Function(Route<dynamic> route, dynamic result);

/// Signature for the [Navigator.onDidRemovePage] callback.
///
/// This must properly update the pages list the next time it is passed into
/// [Navigator.pages] so that it no longer includes the input `page`.
/// (Otherwise, the page will be interpreted as a new page to show when the
/// [Navigator.pages] list is next updated.)
typedef DidRemovePageCallback = void Function(Page<Object?> page);
```

The differences between the two are:

* `onPopPage`
    
    * This callback is called before the pop is executed. With the `boolean` return value we can instruct the Navigator whether or not to actually perform the pop. (In my old code, I always returned false and then handled the pop in my own code, informing Flutter if the stack did change.)
        
    * This callback is called only for the topmost route.
        
* `onDidRemovePage`
    
    * This callback is called after the pop transition has started. There is no way to prevent the pop.
        
    * This callback is called individually for each and every page that leaves the stack. We can have the same behaviour as `onPopPage` if we check if the provided page parameter corresponds to the current topmost page. Although I’m a bit concerned about race conditions..
        

I have seen some problems in the past with `onPopPage` and using back gestures, where the animation does not finish and the two pages get stuck. But this is something I haven’t investigated further. Also because `onPopPage` is deprecated after v3.16.0-17.0.pre.

## Disable back gestures and back button

If you know in advance that the page can not be popped, you can use the `PopScope` widget.

```dart
  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      child: Scaffold(),
    );
  }
}
```

**Downside:** This will disable the back-gestures, but a standard `AppBar` still shows a clickable `BackButton` that does nothing, and that is confusing.

## Disable back-gestures but respond to back button

If you want to disable the back gestures, but still want to respond to `BackButton` events, you can also use the `PopScope` widget, but with the `onPopInvokedWithResult` property.

```dart
  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvokedWithResult:(didPop, result) => controller.didPop(),
      child: Scaffold(),
    );
  }
}
```

**Downside:** we have to do this trickery on each page. It is also a shame that we lose support for back-gestures.

## What if we are stubborn and want it all?

What if I want the following:

* Have back-gestures enabled.
    
* A working `BackButton`.
    
* No widgets like \`PopScope\` to do some magic.
    
* Use the new `onDidRemovePage` callback.
    
* Alter the page stack to my liking (like undo the pop) after onDidRemovePage has been called.
    

### With the back-gesture it works surprisingly well.

Here I made a video, where I show a few times a successful back-gesture, and then a few times a cancelled back-gesture. I swiped the page to 2/3 to the right and then let go.

%[https://youtu.be/MNOsZaGpMWI] 

The navigator animates nicely and naturally back to the original screen.

### With the BackButton it works not that well.

Here I made a video, where I show a few times a successful back button event, and a few times a cancelled back button event.

%[https://youtu.be/wiDYKkpgAnM] 

The navigator quickly animates back and forth between the pages. This looks really terrible.

It seems we need both `onPopPage` and `onDidRemovePage`, but one callback for the back button and the other for back-gestures.

* **Back button**  
    callback: **onPopPage**  
    We return false to inform the `Navigator` we handle the pop ourselves. The transition animation is therefore not started yet. If we want to honour the pop we can inform the `Navigator` about the new screen stack by calling `notifyListeners`, otherwise we do nothing.
    
* **Back-gesture**
    
    **callback: onDidRemovePage**  
    We can cancel the event by just notifying the `Navigator` about the ‘new’ screen stack, by calling `notifyListeners`.
    

You can find the package that I am working on here, with a working sample.  
[https://github.com/jsroest/rubigo\_router](https://github.com/jsroest/rubigo_router)

Let me know what you think!

Sander

»As of the time of this writing, the stable version of Flutter is V3.27.3«
