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

2

6

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?

Undesirable answered 10/1, 2023 at 13:54 Comment(0)
U
6

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.

Undesirable answered 13/1, 2023 at 13:25 Comment(1)
Additionally they are currently discussing a proper solution (as of June 2023). You can follow the updates for this issue here: github.com/flutter/flutter/issues/99112#issuecomment-1583716851Undesirable
R
1

To capture the back button event with GoRouter - including for web browser back button:

  • In the GoRouter declaration, you can set a RouteObserver like this one:
     GoRouter(
       observers: [
        NavigationRouteObserver(),
      ],
      routes: // ..
    );
  import 'package:flutter/material.dart';

  class NavigationRouteObserver extends RouteObserver<PageRoute<dynamic>> {

    @override
    void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
      print('didPop()');

      // The business logic needed on back button
    }
  }
Ruthanneruthe answered 23/11, 2023 at 0:11 Comment(2)
Thanks, interesting. Makes sense to include a RouteObserver - does it essentially function the same as the backButtonDispatcher?Undesirable
I tried with backButtonDispatcher first, from the example you posted, wasn't able to get that working as expected in my local setup. Conceptually tho, yea, seems the same idea here .. just a different place in the stack to support this listenerRuthanneruthe

© 2022 - 2024 — McMap. All rights reserved.