Is there any way intercept 'Back' keydown

2020-02-05 07:16发布

问题:

I need to show an alert dialog before user navigates away from current route by pressing Back button on Android devices. I tried to intercept back button behavior by implementing WidgetsBindingObserver in widget state. There is an closed issue on GitHub regarding same topic. However my code is not working as the method didPopRoute() was never called. Here is my code below:

import 'dart:async';

import 'package:flutter/material.dart';

class NewEntry extends StatefulWidget {
  NewEntry({Key key, this.title}) :super(key: key);

  final String title;

  @override
  State<StatefulWidget> createState() => new _NewEntryState();
}

class _NewEntryState extends State<NewEntry> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Future<bool> didPopRoute() {
    return showDialog(
      context: context,
      child: new AlertDialog(
        title: new Text('Are you sure?'),
        content: new Text('Unsaved data will be lost.'),
        actions: <Widget>[
          new FlatButton(
            onPressed: () => Navigator.of(context).pop(true),
            child: new Text('No'),
          ),
          new FlatButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: new Text('Yes'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.edit),
        onPressed: () {},
      ),
    );
  }
}

回答1:

I found the solution is to use WillPopScope widget. Here is the final code below:

import 'dart:async';

import 'package:flutter/material.dart';

class NewEntry extends StatefulWidget {
  NewEntry({Key key, this.title}) :super(key: key);

  final String title;

  @override
  State<StatefulWidget> createState() => new _NewEntryState();
}

class _NewEntryState extends State<NewEntry> {

  Future<bool> _onWillPop() {
    return showDialog(
      context: context,
      child: new AlertDialog(
        title: new Text('Are you sure?'),
        content: new Text('Unsaved data will be lost.'),
        actions: <Widget>[
          new FlatButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: new Text('No'),
          ),
          new FlatButton(
            onPressed: () => Navigator.of(context).pop(true),
            child: new Text('Yes'),
          ),
        ],
      ),
    ) ?? false;
  }

  @override
  Widget build(BuildContext context) {
    return new WillPopScope(
      onWillPop: _onWillPop,
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text(widget.title),
        ),
        floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.edit),
          onPressed: () {},
        ),
      ),
    );
  }
}


回答2:

The back_button_interceptor package can simplify this for you and is especially useful in more complex scenarios.

https://pub.dev/packages/back_button_interceptor#-readme-tab-

Example usage:

@override
void initState() {
   super.initState();
   BackButtonInterceptor.add(myInterceptor);
}

@override
void dispose() {
   BackButtonInterceptor.remove(myInterceptor);
   super.dispose();
}

bool myInterceptor(bool stopDefaultButtonEvent) {
   print("BACK BUTTON!"); // Do some stuff.
   return true;
}