Search code examples
flutterdartflutter-layoutflutter-navigation

WillPopScope indiscriminately blocking all navigation, whether onWillPop returns true or false


I'm building a login screen. On that screen, I have a "login" button. After this button is pressed, my app connects to the internet in order to check if the user can be logged in. I want navigating backwards (physical button on Android, swipe-back on iOS) to be disabled while this loading is happening.

To achieve this, I should be able to wrap my screen in a WillPopScope widget, and have its onWillPop parameter look like this:

return WillPopScope(
      onWillPop: () async => isLoading ? false : true,
      child: child,
    );

(isLoading is whether or loading is happening, and if navigation should be blocked)

However, this just universally blocks all navigation no matter if isLoading is true or false.


I've also tried this:

return isLoading
        ? WillPopScope(
            onWillPop: () async => false,
            child: child,
          )
        : child;

This works, however, doing it this way will block all animations in the child, which basically renders the solution useless.

Is there anyway to get the first method to work? Or, is there another way all together?

Thanks.


Solution

  • Figured it out. Use this package: https://pub.dev/packages/back_button_interceptor/example.

    Create a widget that wraps your screen (Scaffold), using it like this:

    import 'package:back_button_interceptor/back_button_interceptor.dart';
    import 'package:flutter/material.dart';
    
    class NavBlocker extends StatefulWidget {
      const NavBlocker({
        super.key,
        required this.blocking,
        required this.child,
      });
    
      final bool blocking;
      final Widget child;
    
      @override
      State<NavBlocker> createState() => _NavBlockerState();
    }
    
    class _NavBlockerState extends State<NavBlocker> {
      @override
      void initState() {
        super.initState();
        BackButtonInterceptor.add(myInterceptor);
      }
    
      @override
      void dispose() {
        BackButtonInterceptor.remove(myInterceptor);
        super.dispose();
      }
    
      bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
        return widget.blocking;
      }
    
      @override
      Widget build(BuildContext context) {
        return widget.child;
      }
    }
    

    Where blocking specifies whether or not navigation should be blocked or not.

    This solution enables animations to work, too!