Search code examples
flutterflutterwebviewpluginflutter-go-router

Flutter Webview with GoRouter (navigation 2.0) - how to handle back button press


According to this blog, if using Navigator 2.0 and/or (in my case) GoRouter you can no longer override the phone's back button using the "WillPopScope" and onWillPop function call. Navigator 2.0 now uses PopRoute to go back.

This causes an issue when using webview on a flutter page. If the user navigates to another web page within that webview and then clicks the back button on their phone they naturally expect the webview navigate back to the previous web page. But instead it takes the user off that page and back to their previous flutter page.

Is there any way around this? Can I have my back button first check whether there is a controller.canGoBack() like I used to be able to do with the old Navigator system?


Solution

  • I have found a solution. Convoluted, but functional:

    I had to create a custom "backButtonDispatcher" and add it to the main.dart MaterialApp.router function

    child: Builder(builder: (BuildContext context) {
        final router = Provider.of<MainRouter>(context, listen: false).router;
        backbuttondispatcher = backButtonDispatcher(router.routerDelegate, settings);
    
        return MaterialApp.router(
          routeInformationParser: router.routeInformationParser,
          routeInformationProvider: router.routeInformationProvider,
          routerDelegate: router.routerDelegate, 
          backButtonDispatcher: backbuttondispatcher,
          .
          .
          .
    

    I created the new dispatcher in the router folder and called it "backbuttondispatcher.dart.

    import 'package:flutter/material.dart';
    
    class backButtonDispatcher extends RootBackButtonDispatcher {
    
         final RouterDelegate _routerDelegate;
         final _settings;
    
    backButtonDispatcher(this._routerDelegate,this._settings)
      : super();
    
    Future<bool> didPopRoute() async {
      //Can user leave the page?
      if (!_settings.canLeavePage) {
        //no, as the webview widget has flagged canLeavePage as false
        _settings.goBackToPreviousWebsite();
        return true;
      }else{
        //yes, perform standard popRoute call
        return _routerDelegate.popRoute();
      }
     }
    }
    

    Using a shared class reference (I used "_settings") I store a flag that says whether or not the user has traversed through more than one web page - if TRUE, the back button dispatcher won't go back to a previous route/page and instead call another function (pointer) that handles going back to a previous web page in the webview widget route. But if FALSE, the dispatcher performs it's standard didPopRoute function.

    Additionally, on all other routes/pages with a webview, the pointer function and boolean need to reset to null and false. This is not ideal but fortunately there aren't that many pages in the app.

    It annoys me that they changed the back button functionality for main route/page navigation but didn't take in to consideration the fact that the back button can also be used for going back to a previous webpage. I understand that we shouldn't really be showing web pages with apps anyway but we lowly developers don't always have the power to deny app requirements from higher up.