Working with WillPopScope Widget

We can notice that a few apps have a feature where if we use the back button to exit the app, a message appears with the text “Please click BACK again to exit.” This feature can be achieved by controlling the back button. In flutter, we have a widget called WillPopScope that allows us to control the back button navigation. In this article, we are going to work on WillPopScope Widget.

WillPopScope constructor :

const WillPopScope({
    Key? key,
    required this.child,
    required this.onWillPop,
  })

WillPopScope contains three parameters: key, child, and onWillpop, as shown in the code above.

Key: A Widget key is an identifier of the widget.

this.child: This is a mandatory property; we must mention the child that represents the screen widget on which the back button behavior can be controlled.

this.onWillPop: onWillPop is a callback method that returns a Future value; if true, the screen can be popped; if false, the screen will not be popped out. However, the screen can still be popped by calling the Navigator.pop(context).

Disabling Back Navigation

As we discussed above, We can disable it by giving the false value as a returned value to the onWillPop method.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("WillPopScope Demo"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context)
                .push(MaterialPageRoute(builder: (context) => SecondScreen()));
          },
          child: const Text("Go to Next screen"),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  const SecondScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        return false;
      },
      child: Scaffold(
        appBar: AppBar(
          title: const Text("Second screen"),
        ),
        body: Container(
          child: const Center(
            child: Text("Second Screen"),
          ),
        ),
      ),
    );
  }
}

We have two screens if we look at the code above. The “Go to Next Screen” button can be used to navigate to the second screen. I wrapped the second screen in a WillPopScope widget and set onWillPop’s returned value to false. As a result, the second screen cannot be popped out by pressing the back button.

The screen can still be popped by calling the Navigator.pop(context). Even though the return value of onWillPop method is false

WillPopScope(
      onWillPop: () async {
        Navigator.pop(context);
        return false;
      },
      child: Scaffold(
        appBar: AppBar(
          title: const Text("Second screen"),
        ),
        body: Container(
          child: const Center(
            child: Text("Second Screen"),
          ),
        ),
      ),
    );

Implement a feature that ask the user to “Press the back button again to exist the app”

Sometimes the user clicks the back button unexpectedly, where the app can exist as result, but the user’s actual intention is not to close the app. In order to quit the app, we want to require a second click for the back button. We’ll calculate the time gap between two clicks, and if it’s less than 2 seconds, we can assume that the user wants to quit the app, so we’ll close it by passing true as the return value to the onWillPop method, and if it’s more than 2 seconds, we’ll keep the return value is false. and sends the message to the user as “Press the back button again to exit the app” using Toast or snack bar widget.

The complete source code is provided below to obtain the required functionality.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    DateTime _lastExitTime = DateTime.now();
    return WillPopScope(
      onWillPop: () async {
        if (DateTime.now().difference(_lastExitTime) >= Duration(seconds: 2)) {
          //showing message to user
          final snack =  SnackBar(
            content:  Text("Press the back button again to exist the app"),
            duration: Duration(seconds: 2),
          );
          ScaffoldMessenger.of(context).showSnackBar(snack);
          _lastExitTime = DateTime.now();
          return false; // disable back press
        } else {
          return true; //  exit the app
        }
      },
      child: Scaffold(
        appBar: AppBar(
          title: const Text("WillPopScope Demo"),
        ),
        body: Center(
          child: Text("FlutterAnt.com"),
        ),
      ),
    );
  }
}

We will get the following output after running the above code.

For reference click here

Thanks for the Reading … 🙂

Leave a Comment