Search code examples
androidiosflutterwidgetflutter-go-router

Flutter GoRouter PopScope Widget Not Working


I'm working on a Flutter application using go_router for navigation and PopScope to handle back navigation. I'm experiencing issues with PopScope not behaving as expected.

Context:

  • I'm using go_router for routing in my Flutter app.

  • I need to show an AlertDialog when the user tries to navigate back from a specific route, but PopScope doesn't seem to work as intended.

  • I want to control the behavior when the back button is pressed and either allow or prevent the navigation based on user confirmation.

Code Example:

Here is the relevant code snippet where I configure PopScope and GoRouter:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

class AppNavigation {
  AppNavigation._();

  static final rootNavigatorKey = GlobalKey<NavigatorState>();

  static List<RouteBase> routes = [
    GoRoute(
      path: "/home",
      name: "Home",
      builder: (BuildContext context, GoRouterState state) {
        return HomePage();
      },
    ),
    // Other routes...
  ];

  static final GoRouter router = GoRouter(
    navigatorKey: rootNavigatorKey,
    routes: routes,
  );
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: PopScope(
        onPopInvoked: () async {
          final shouldExit = await showDialog(
            context: context,
            builder: (context) => AlertDialog(
              title: Text('Confirmation'),
              content: Text('Do you want to exit?'),
              actions: [
                TextButton(
                  onPressed: () => Navigator.of(context).pop(false),
                  child: Text('No'),
                ),
                TextButton(
                  onPressed: () => Navigator.of(context).pop(true),
                  child: Text('Yes'),
                ),
              ],
            ),
          );
          return shouldExit;
        },
        child: Center(child: Text('Home Page Content')),
      ),
    );
  }
}

Issue:

  • The PopScope widget is supposed to intercept the back navigation and display an AlertDialog, but it doesn't seem to work as expected.

  • The AlertDialog does not appear when the back button is pressed, and the navigation behavior is not controlled.

What I’ve Tried:

  • Ensured that PopScope is correctly used within the Navigator widget.

  • Verified that GoRouter is properly set up and that the route configuration is correct.

Question:

How can I make sure that PopScope works properly with go_router? Is there a known issue or workaround for using PopScope with go_router to control back navigation?

Thank you for any help or insights!

What I Tried:

  • Implemented PopScope to handle back navigation within the GoRouter setup.

  • Used PopScope to show an AlertDialog asking the user for confirmation when they attempt to navigate back.

  • Configured the onPopInvoked callback in PopScope to display the AlertDialog and determine if navigation should proceed.

What I Expected:

  • When the back button is pressed, PopScope should intercept the event and show the AlertDialog.

  • The AlertDialog should give the user the option to either confirm or cancel the navigation.

  • If the user confirms, navigation should proceed. If the user cancels, the navigation should be blocked.

What Actually Happened:

  • The AlertDialog does not appear when the back button is pressed.

  • Navigation occurs immediately without showing the dialog, ignoring the PopScope configuration.


Solution

  •  void _onBackPressed({required BuildContext context}) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) {
              showDialog(
                context: context,
                builder: (context) {
                  return AlertDialog(
                    title: const Text('Are you sure?'),
                    content: const Text('Do you want to end the call?'),
                    actions: [
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        child: const Text('No'),
                      ),
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context);
                          Navigator.pop(context);
                          openCallEndReviewBottomSheet(context, navigationProvider);
                        },
                        child: const Text('Yes'),
                      ),
                    ],
                  );
                },
              );
            }
          });
        }
    
        return BackButtonListener(
          onBackButtonPressed: () async {
            _onBackPressed(context: context);
            return true;
          },
    /// ...  My Solution