CodeX
Published in

CodeX

Catching Back Button Presses on Android using Navigator 2.0

A Flutter Case Study

Welcome to the first in a new series of articles on Flutter. I have recently started browsing StackOverflow for Flutter related that I might be able to answer in an effort to help the community of budding Flutter enthusiasts. Every now and then, I come across a question that I feel warrants an article explaining the problem and solution, and perhaps a few details as to why the issue arises to begin with. I have dubbed these articles Case Studies, and I look forward into digging into the intriguing, obscure, or far-too-common issues that I come across. So without further ado, let’s dive into our first case study.

If you found this article worthy of a read from the title, then you have undoubtedly had experience catching back button presses on Android in Flutter pre-Navigator 2.0, but here’s a little refresher. We simply wrap our widget tree in a WillPopScope widget and provide an onWillPop callback. Here is an example that catches the back button press and provides an AlertDialog to confirm app exit.

WillPopScope(
onWillPop: () async {
final shouldPop = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Exit App'),
content: Text('Are you sure you want to leave the app?'),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () => Navigator.pop(context, false),
),
TextButton(
child: Text('Confirm'),
onPressed: () => Navigator.pop(context, true),
),
],
);
},
);
// if the dialog is dismissed by tapping outside of the barrier
// the result is null, so we return false
return shouldPop ?? false;
},
child: // continue with widget tree here
)

As you can see, the user’s input in the AlertDialog will tell us whether they actually want to leave the app, and we can pass that result along to the WillPopScope widget.

If you’ve tried this approach while using Navigator 2.0, then you’ve discovered that WillPopScope no longer catches back button presses. This is because with Navigator 2.0, RouterDelegate's popRoute method is responsible for handling requests from the operating system to pop the current route (back button presses), and we can see in the method’s documentation:

The method should return a boolean [Future] to indicate whether this delegate handles the request. Returning false will cause the entire app to be popped.

The implementation by design pops the entire app immediately upon a press of the back button. Therefore, we must override it when extending the RouterDelegate class and handle back button presses according to our needs.

@override
Future<bool> popRoute() async {
// check if we have pages in the stack to pop before
// attempting app exit
if (_pages.length > 1) {
// handle popping the current page off of the stack
return Future.value(true);
}
final result = await showDialog<bool>(
// get the context from the navigatorKey defined
// in your RouterDelegate class
context: navigatorKey!.currentContext!,
builder: (context) {
return AlertDialog(
title: const Text('Exit App'),
content: const Text('Are you sure you want to exit the app?'),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: () => Navigator.pop(context, true),
),
TextButton(
child: const Text('Confirm'),
onPressed: () => Navigator.pop(context, false),
),
],
);
},
);
// if the dialog is dismissed by tapping outside of the barrier
// the result is null, so we return false
return shouldPop ?? false;
}

If you do not need back button presses to navigate backwards in your app and need it to present the exit app dialog at all times, you may omit the first if statement.

NOTE: You may have noticed that the results from the dialog are reversed in the two approaches. This is because when using WillPopScope, we are telling the widget if it should pop the scope from the Navigator (true = pop, false = don’t pop). When using Navigator 2.0, we are telling the Router whether or not we have handled the request (true = handled, false = not handled). When we report that we have not handled it ourselves, the Router handles it by exiting the app.

That’s all there is to it. By moving your logic from WillPopScope's onWillPop callback, to RouterDelegate's popRoute method, you can easily handle back button presses while using Navigator 2.0.

If you would like a deeper dive into Navigator 2.0 with an in-depth tutorial on converting an app to Navigator 2.0, check out my 3 part series starting with A Simpler Guide to Flutter Navigator 2.0: Part I.

Thank you for reading! If you found this article helpful and would like to read more case studies, please clap and follow. Happy coding!

--

--

--

Everything connected with Tech & Code. Follow to join our 900K+ monthly readers

Recommended from Medium

Tutorial Fuzzy Logic Mamdani for Arduino

Tutorial Fuzzy Logic Mamdani for Arduino

Securing your workflow using Vault Agent with GCP Auth Method on HashiCorp Vault

Attaching PDF template to email in SuiteCRM

Text Tool For Mac

Creating a file browser on top of S3 private bucket for private contents without access key IDs and…

Adding Sign In with Apple to your app in under 5mins with ZERO code

Industrial Use Case On Automation Using Ansible

Sciwork Sprint with Career Panel at NYCU on May 13th

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Lee Phillips

Lee Phillips

Software developer. Flutter fanatic. Other interests include photography, sports, coffee, and food.

More from Medium

Divider in Flutter

How to use Flutter Time Series Charts with Firebase

How to integrate Paystack Payment Gateway in Flutter App ?

Firebase — Flutter💙💛