skip to Main Content

Using PopScope to make sure that the user need to press back button twice to quit the app. The issue is after the back button pressed twice, it only returns blank screen or empty screen instead of quitting the app. If I’m using WillPopScope, it is working as intended.

I also have make sure that my app only using 1 MaterialApp within the app. The question is how to correctly implement this behavior for Android and iOS?

Here’s some example of the code:

class RootScaffold extends StatefulWidget {
  final Widget body;

  const RootScaffold({required this.body, super.key});

  @override
  State<RootScaffold> createState() => _RootScaffoldState();
}

class _RootScaffoldState extends State<RootScaffold> {
  DateTime? lastBackPressTime;

  bool onWillPop() {
    DateTime now = DateTime.now();
    if (lastBackPressTime == null ||
        now.difference(lastBackPressTime!) > const Duration(seconds: 2)) {
      lastBackPressTime = now;
      Fluttertoast.showToast(msg: 'Press back again to exit.');
      return false;
    }

    return true;
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvoked: (bool didPop) async {
        if (didPop) {
          return;
        }

        final shouldPop = onWillPop();
        if (shouldPop && context.mounted) {
          Navigator.of(context).pop();
        }
      },
      child: Scaffold(body: widget.body),
    );
  }
}

2

Answers


  1. There are 2 issues here, I believe it is possible to solve you problem on Android, but on iOS it can be tricky.

    So, for Android: change Navigator.of(context).pop(); to SystemNavigator.pop();. The problem here is that Navigator.of can not trigger system mechanics to exit app. It will just pop flutter screen, that why you see white screen.

    For iOS it will not work this way, since SystemNavigator.pop() will do nothing. It is intended behaviour since it goes against Apple’s design guidelines: Apple encourages users to exit apps using the home button or the swipe gesture.
    I think, in this situation, proper way is to do nothing on iOS.

    Login or Signup to reply.
  2. Try to make onWillPop() method Future<bool> and the and use Future.value([FutureOr<T>? value]) constructor to return the value.

    Like this :

    class ClickAgainToCloseWidget extends StatefulWidget {
      const ClickAgainToCloseWidget({super.key});
    
      @override
      State<ClickAgainToCloseWidget> createState() =>
          _ClickAgainToCloseWidgetState();
    }
    
    class _ClickAgainToCloseWidgetState extends State<ClickAgainToCloseWidget> {
      DateTime? lastBackPressTime;
    
      Future<bool> onWillPop({int neededTime = 2, required String msgToast}) {
        DateTime now = DateTime.now();
        if (lastBackPressTime == null ||
            now.difference(lastBackPressTime!) >
                Duration(
                  seconds: neededTime,
                )) {
          lastBackPressTime = now;
          Fluttertoast.showToast(msg: msgToast);
          return Future.value(false);
        }
    
        return Future.value(true);
      }
    
      @override
      Widget build(BuildContext context) {
        return PopScope(
          canPop: false,
          onPopInvoked: (bool didPop) async {
            if (didPop) {
              return;
            }
    
            final shouldPop =
                await onWillPop(msgToast: 'Press back again to exit.');
            if (shouldPop && context.mounted) {
              Navigator.of(context).pop();
            }
          },
          child: const Scaffold(
            body: Center(
              child: Text("Click Back Again To close"),
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search