有什么方法可以在 Android 上拦截 Flutter 应用程序中的 'Back' keydown 吗?

Is there any way intercept 'Back' keydown in Flutter app on Android?

我需要在用户通过按 Android 设备上的 Back 按钮离开当前路线之前显示一个警告对话框。我试图通过实现从未调用 WidgetsBindingObserver in widget state. There is an closed issue on GitHub regarding same topic. However my code is not working as the method didPopRoute() 来拦截后退按钮行为。下面是我的代码:

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: () {},
      ),
    );
  }
}

我发现解决方案是使用 WillPopScope 小部件。下面是最终代码:

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: () {},
        ),
      ),
    );
  }
}

back_button_interceptor 包可以为您简化这一过程,在更复杂的场景中尤其有用。

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

用法示例:

@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;
}

如果您使用的是 GetX 包并实现了 GetMaterialApp 方法来初始化您的应用程序,则永远不会调用 WidgetsBindingObserver 中的 didPopRoutedidPushRoute 方法。请改用 routingCallback,下面是一个示例,有关更多信息,请查看 GetX documentation:

GetMaterialApp(
  routingCallback: (routing) {
    routing.isBack ? didPopRoute() : didPushRoute(routing.current);
  }
)