【问题标题】:Is there any way intercept 'Back' keydown in Flutter app on Android?有没有办法在 Android 上的 Flutter 应用程序中拦截“返回”按键?
【发布时间】:2017-11-21 23:00:03
【问题描述】:

在用户通过按 Android 设备上的Back 按钮离开当前路线之前,我需要显示一个警报对话框。我试图通过在小部件状态下实现WidgetsBindingObserver 来拦截后退按钮的行为。在 GitHub 上有一个关于同一主题的封闭 issue。但是,我的代码无法正常工作,因为从未调用过 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: () {},
      ),
    );
  }
}

【问题讨论】:

    标签: android dart flutter


    【解决方案1】:

    我发现解决方案是使用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: () {},
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 嗨,马尔,我在上面尝试了 onWillPop 方法,当我在 willPop 上运行发布版本时,它只工作调试 apk 未调用。
    • 函数中的 _onWillPop ?? false 应该被移除
    • 我的应用程序使用 Navigator.PushReplacementNamed 来移动它的流程。我学会了总是从 OnWillPop() 返回 Future.value(false) 以防止 Flutter 弹出导航堆栈(它不存在)。在我开始遵循这项政策之前,我的结果非常不稳定。
    • onWillPop: _onWillPop也可以是onWillPop: () =&gt; _onWillPop()
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-06
        • 1970-01-01
        • 1970-01-01
        • 2014-08-26
        • 2021-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多