Using WillPopScope in Flutter for Android navigation – LogRocket Blog


Introduction

You’re on your favorite app, scrolling through the contents, you’ve spent a couple of minutes enjoying the content, which I might add, is captivating. Suddenly, while reading a particular piece of content, the page pops back. Surprising as it is, you discover that your finger brushed over the back button.

This may not be much of an issue, if you can just go forward to the page and continue from where you stopped, but if the app has refreshed the screen or is set to bring up newer content any time you enter the page — well, then, you’ve lost the position on screen or the place you were while scrolling through that particular content.

These and many more crucial things (well … that particular tweet was important to me) have an impact on the overall user experience. Taking measures to prevent these occurrences should definitely be on your list of things to do when building an application.

In this article, we’ll be looking at one of such measures in this article: The WillPopScope widget. Specifically, we’ll cover:

Shall we?

  WillPopScope WillPopScope(
    required Widget child,
    required Future<bool> Function()? onWillPop,
  )

The WillPopScope widget comes with the Flutter framework. It gives us control over the back button action, allowing the current page to go back to the previous one if it meets certain requirements. This is achieved using a callback, which the widget takes in as one of its parameters.

As described in the Flutter official documentation, the widget:

Registers a callback to veto attempts by the user to dismiss the enclosing ModalRoute

With the WillPopScope widget, you can effectively prevent situations similar to the one described in the Introduction: by providing an extra means of verification, users can choose to cancel/prevent their current page from popping off, i.e., going back.

The WillPopScope widget requires two parameters:

  • The onWillPop callback: handles the action and determines if the page should be popped or not using a boolean; when it’s true, the page pops back, and if not, it remains on the same page
  • The child widget: the widget for the view

In the next section, we’ll use these in an actual application. Let’s get to it.

How to use WillPopScope in a Flutter app

We are going to build a sample app to demonstrate the WillPopScope widget in action. The sample app contains two pages, the homeView and the contentView.

The homeView has some text and a button. We’ll use the button to navigate to the next page, which is the contentView and contains the WillPopScope widget.

WillPopScope will capture the back button and perform an action any time the back button is clicked while on this view. As mentioned earlier, the WillPopScope widget has a callback, inside of which we’ll display an alert dialog with a question (Do you want to go back?) and two action options (a Yes button and a No button). These actions determine whether the page should pop or not.

With that cleared up, let’s get coding.

Creating and setting up the Flutter app

First create a new project and generate the necessary files and folders using the command below:

flutter create willpopscope_tutorial

Once that’s done, go into the main.dart file in the lib folder. This is the entry point to your application. Delete the home widget and return HomeView.

We’ll create HomeView widget in the next step. You can also adjust the title for the MaterialApp to display what you want.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget 
  const MyApp(Key? key) : super(key: key);
  @override
  Widget build(BuildContext context) 
    return const MaterialApp(
     title: "WillPopScope Article",
     home: HomeView(),
   );
  

Creating the HomeView

Next, create a Stateless widget named HomeView. This is the initial view for our sample application. It should contain a Text and a TextButton. The TextButton will be used to navigate to the next view, which is the ContentView.

class HomeView extends StatelessWidget 
  const HomeView(Key? key) : super(key: key);
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text('HomeView'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Text(
              'HomeView',
              style: TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(
              height: 24,
            ),
            TextButton(
              style: ButtonStyle(
                backgroundColor: MaterialStateProperty.all<Color>(Colors.blue),
              ),
              onPressed: () 
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) 
                      return const ContentView();
                    ,
                  ),
                );
              ,
              child: const Text(
                'Go To ContentView',
                style: TextStyle(
                  color: Colors.white,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  

Creating the ContentView widget

Next, create another Stateless widget, and name it ContentView. This is the widget on which we’ll use the WillPopScope.

class ContentView extends StatelessWidget 
  const ContentView(Key? key) : super(key: key);
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Column(
        children: [],
      ),
    );
  

Implementing the WillPopScope widget on our views

Wrap the Scaffold widget with the WillPopScope widget. It takes in the onWillPop callback, captures the back button action, and performs an action when the user clicks the back button.

return WillPopScope(
  onWillPop: (),
  child: Scaffold(
    body: Column(
      children: [],
    ),
  ),
);

In the onWillPop callback, we’ll do two things:

  1. Display an alert dialog to the user with a question and ask them to click an action button (either Yes or No)
  2. Use the response from the dialog to pop the ContentView

In order to get the user’s selection, we assign the result from the dialog to a boolean variable. In the ActionButtons, we pass in the data (true for Yes and false for No) into Navigator.pop(context, true/false). The value is then passed to the shouldPop variable.

Next, we return the shouldPop variable, which should be either true or false and will be used to determine if the ContentView should pop (when shouldPop is true) or not (when shouldPop is false).

  onWillPop: () async 
        final shouldPop = await showDialog<bool>(
          context: context,
          builder: (context) 
            return AlertDialog(
              title: const Text('Do you want to go back?'),
              actionsAlignment: MainAxisAlignment.spaceBetween,
              actions: [
                TextButton(
                  onPressed: () 
                    Navigator.pop(context, true);
                  ,
                  child: const Text('Yes'),
                ),
                TextButton(
                  onPressed: () 
                    Navigator.pop(context, false);
                  ,
                  child: const Text('No'),
                ),
              ],
            );
          ,
        );
        return shouldPop!;
      ,

With this, we’re done with the ContentView widget. The code for the full widget should look like this:

class ContentView extends StatelessWidget {
  const ContentView(Key? key) : super(key: key);
  @override
  Widget build(BuildContext context) 
    return WillPopScope(
      onWillPop: () async 
        final shouldPop = await showDialog<bool>(
          context: context,
          builder: (context) 
            return AlertDialog(
              title: const Text('Do you want to go back?'),
              actionsAlignment: MainAxisAlignment.spaceBetween,
              actions: [
                TextButton(
                  onPressed: () 
                    Navigator.pop(context, true);
                  ,
                  child: const Text('Yes'),
                ),
                TextButton(
                  onPressed: () 
                    Navigator.pop(context, false);
                  ,
                  child: const Text('No'),
                ),
              ],
            );
          ,
        );
        return shouldPop!;
      ,
      child: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: const Text('Content View'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: const [
              Text(
                'This is the Content View',
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  
}

With this done, we’ve fully setup the WillPopScope widget and can prevent unintentional popping off of a particular view!

Testing our sample app

Let’s test out our sample app. Run the command below to run the application.

flutter run

Conclusion

Yay! You’ve now seen how we can practically solve navigation issues and fine-tune the user’s browsing experience using the WillPopScope widget. From displaying dialog confirmations to performing custom actions to disposing streams, and more, there is a wide range of things you can do with this widget, such as display a snackbar or toast message. Explore your options as much as you can and use them in building your next Flutter app.

Check out the complete source code for the sample app. If you have any questions or inquiries, feel free to reach out to me on Twitter: @Blazebrain01 or LinkedIn: Blazebrain.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.





Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Marrakech added to 2021-22 FE calendar as Vancouver replacement

Next Article

‘Our ancestors are in the rocks’: Australian gas project threatens ancient carvings – and emissions blowout

Related Posts