【问题标题】:How to deactivate or override the Android "BACK" button, in Flutter?如何在 Flutter 中停用或覆盖 Android“BACK”按钮?
【发布时间】:2018-02-05 14:02:44
【问题描述】:

有没有办法在特定页面上停用 Android 后退按钮?

class WakeUpApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Time To Wake Up ?",
      home: new WakeUpHome(),
      routes: <String, WidgetBuilder>{
        '/pageOne': (BuildContext context) => new pageOne(),
        '/pageTwo': (BuildContext context) => new pageTwo(),
      },
    );
  }
}

在 pageOne 我有一个按钮可以转到 pageTwo:

new FloatingActionButton(
  onPressed: () {    
    Navigator.of(context).pushNamed('/pageTwo');
  },
)

我的问题是,如果我按 android 屏幕底部的后退箭头,我会回到 pageOne。我希望这个按钮根本不显示。 理想情况下,除非用户将手指按住屏幕 5 秒钟,否则我希望无法离开此屏幕。 (我正在尝试为幼儿编写一个应用程序,并且希望只有父母能够导航出特定的屏幕)。

【问题讨论】:

    标签: flutter


    【解决方案1】:

    答案是WillPopScope。它将防止页面被系统弹出。你仍然可以使用Navigator.of(context).pop()

    @override
    Widget build(BuildContext context) {
      return new WillPopScope(
        onWillPop: () async => false,
        child: new Scaffold(
          appBar: new AppBar(
            title: new Text("data"),
            leading: new IconButton(
              icon: new Icon(Icons.ac_unit),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ),
        ),
      );
    }
    

    【讨论】:

    • 对于想要截取Form的弹窗,使用Form的onWillPop属性会更方便。它可以访问表单状态,并且可以首先检查是否有任何用户可能不想丢失的状态。
    • 嗨@RémiRousselet,你能帮我管理后台堆栈,就像我有一堆 A、B、C、D 屏幕,我想从 D->B 或 D- 导航>A ,那我将如何管理它。你能指导我吗
    • 我知道这个答案可能已经过时了。不过是GEM!考虑扩大解释。它工作得很好。谢谢。
    • 如果有人想在 pop() 之前做点什么,可以使用这个onWillPop: () async { onBack(); // "DO YOUR FUNCTION IS HERE WHEN POP" return false; }
    【解决方案2】:

    正如 Rémi Rousselet 指出的那样,WillPopScope 通常是要走的路。但是,如果您正在开发一个应该直接响应后退按钮的有状态小部件,您可以使用:

    https://pub.dartlang.org/packages/back_button_interceptor

    注意:我是这个包的作者。

    【讨论】:

    • 恕我直言,您不认为这更适合作为评论吗?
    • @CopsOnRoad 不,因为这不是对另一个答案的评论,而是解决同一问题的完全不同的方法。
    • 这是一个很好的选择,如果它是评论的一部分,它可能会被忽略。
    • 这是一个很好的选择,就像一个魅力,感谢您在单独的答案中突出显示它
    【解决方案3】:

    虽然 Remi 的回答是正确的,但通常您不想简单地阻止返回按钮,而是希望用户确认退出。

    您可以通过从确认对话框中获得答案来执行类似的操作,因为onWillPop 是未来。

    @override
    Widget build(BuildContext context) {
      return WillPopScope(
        child: Scaffold(...),
        onWillPop: () => showDialog<bool>(
          context: context,
          builder: (c) => AlertDialog(
            title: Text('Warning'),
            content: Text('Do you really want to exit'),
            actions: [
              FlatButton(
                child: Text('Yes'),
                onPressed: () => Navigator.pop(c, true),
              ),
              FlatButton(
                child: Text('No'),
                onPressed: () => Navigator.pop(c, false),
              ),
            ],
          ),
        ),
      );
    }
    

    【讨论】:

    • 为什么这个答案会被否决?有没有人实施这个并且有不好的经验?
    • 我想在设备上按后退按钮后转到上一个屏幕而不是第一个屏幕。我不需要这样的对话框。我该怎么办
    【解决方案4】:

    您可以使用Future.value(bool) 处理返回按钮。

    bool _allow = true;
    
    @override
    Widget build(BuildContext context) {
      return WillPopScope(
        child: Scaffold(appBar: AppBar(title: Text("Back"))),
        onWillPop: () {
          return Future.value(_allow); // if true allow back else block it
        },
      );
    }
    

    【讨论】:

    • 仅供参考,这相当于onWillPop: () async =&gt; _allow
    • @Lynn 是的,我知道。我只是想分享Future.value() 电话。
    • @CopsOnRoad return SafeArea() 已经定义然后如何处理在颤动中的后压
    • @CopsOnRoad 嗨,在我的类代码中 Widget build(BuildContext context) { return SafeArea( child: Stack( children: [ ..... 所以这里 return WillPopScope( ); 无法定义两个return语句然后如何在flutter中初始化backpressed
    • @s.j 将 SafeArea 小部件包装在 WillPopScope 中。
    【解决方案5】:

    答案可能你知道使用 WillPopScope,但不幸的是,在 IOS 上你不能向后滑动 上一页,让我们自定义您的 MaterialPageRoute:

    class CustomMaterialPageRoute<T> extends MaterialPageRoute<T> {
      @protected
      bool get hasScopedWillPopCallback {
        return false;
      }
      CustomMaterialPageRoute({
        @required WidgetBuilder builder,
        RouteSettings settings,
        bool maintainState = true,
        bool fullscreenDialog = false,
      }) : super( 
              builder: builder,
              settings: settings,
              maintainState: maintainState,
              fullscreenDialog: fullscreenDialog,
            );
    }
    

    现在您可以使用 WillPopScope 并且 向后滑动 将在 IOS 上运行。详细答案在这里:https://github.com/flutter/flutter/issues/14203#issuecomment-540663717

    【讨论】:

      【解决方案6】:

      我在此处发布此内容,以防有人发现此内容并希望他们能找到一个简单的示例 https://gist.github.com/b-cancel/0ca372017a25f0c120b14dfca3591aa5

      import 'package:flutter/material.dart';
      
      import 'dart:async';
      
      void main() => runApp(new BackButtonOverrideDemoWidget());
      
      class BackButtonOverrideDemoWidget extends StatefulWidget{
        @override
        _BackButtonOverrideDemoWidgetState createState() => new _BackButtonOverrideDemoWidgetState();
      }
      
      class _BackButtonOverrideDemoWidgetState extends State<BackButtonOverrideDemoWidget> with WidgetsBindingObserver{
      
        //-------------------------Test Variable
      
        bool isBackButtonActivated = false;
      
        //-------------------------Required For WidgetsBindingObserver
      
        @override
        void initState() {
          super.initState();
          WidgetsBinding.instance.addObserver(this);
        }
      
        @override
        void dispose() {
          WidgetsBinding.instance.removeObserver(this);
          super.dispose();
        }
      
        //-------------------------Function That Triggers when you hit the back key
      
        @override
        didPopRoute(){
          bool override;
          if(isBackButtonActivated)
            override = false;
          else
            override = true;
          return new Future<bool>.value(override);
        }
      
        //-------------------------Build Method
      
        @override
        Widget build(BuildContext context) {
          return new Directionality(
            textDirection: TextDirection.ltr,
            child: new Container(
                color: (isBackButtonActivated) ? Colors.green : Colors.red,
                child: new Center(
                    child: new FlatButton(
                      color: Colors.white,
                      onPressed: () {
                        isBackButtonActivated = !isBackButtonActivated;
                        setState(() {});
                      },
                      child: (isBackButtonActivated) ?
                      new Text("DeActive the Back Button") : new Text("Activate the Back Button"),
                    )
                )
            ),
          );
        }
      }
      

      【讨论】:

      • 我昨天自己真的用过。请记住,它仅适用于 Android,Apple 没有返回按钮。尽管您能否告诉我出了什么问题,以便我可以修复它,或者它仅适用于我的特定模拟器。我已经有几个星期没有更新我的颤振了,所以也许这就是问题所在。告诉我
      • @BryanCancel 仅当您还没有任何推送路线时,您的答案才有效。请参阅方法 WidgetsBinding.handlePopRoute()。它按注册顺序通知观察者,并在收到 true 时立即停止。当有推送的路由时,导航器首先返回 true,然后你的观察者实际上不会被调用。换句话说,您的代码仅用于防止应用程序在用户单击后退按钮时关闭,当没有更多路由时。
      【解决方案7】:

      尝试这样做会杀死您的应用程序状态

      @override
        Widget build(BuildContext context) {
          return WillPopScope(
            ////////////////
            onWillPop: () => showDialog<bool>(
              context: context,
              builder: (c) => AlertDialog(
                title: Text(
                  'Warning',
                  textAlign: TextAlign.center,
                ),
                content: Text('Are you sure to exit?'),
                actions: [
                  TextButton(
                    style: TextButton.styleFrom(
                      primary: Colors.green,
                    ),
                    onPressed: () async {
                      exit(0);// kill app
                    },
                    child: Text('Yes'),
                  ),
                  TextButton(
                    style: TextButton.styleFrom(
                      primary: Colors.red,
                    ),
                    onPressed: () => Navigator.pop(c, false),
                    child: Text('No'),
                  )
                ],
              ),
            ),
            /////////////////////
            child: Scaffold(),
          );
        }
      

      【讨论】:

        【解决方案8】:

        我使用 mixin 和 WillPopScope 小部件无法为我完成工作。 这是我发现的最佳方法,在我看来比 WillPopScope 好得多。
        final bool canPop = ModalRoute.of(context)?.canPop ?? false;
        在 appbar 中这样使用它:

        leading: ModalRoute.of(context)?.canPop ?? false
            ? IconButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                icon: (Platform.isAndroid)
                    ? const Icon(Icons.arrow_back)
                    : const Icon(Icons.arrow_back_ios),
              )
            : Container(),
        

        【讨论】:

          【解决方案9】:

          如果您使用 null 安全性进行编码,这是一个可行的替代解决方案。您需要禁用默认的后退按钮,并将其替换为 IconButton。在此示例中,当用户在退出前单击后退按钮进行确认时,我将推送一个 AlertDialog。您可以替换此功能并将用户发送到任何其他页面

          return WillPopScope(
            onWillPop: () async => false,
            child: Scaffold(
              appBar: AppBar(
                automaticallyImplyLeading: true,
                title: Text(),
                leading: IconButton(
                  icon: Icon(Icons.arrow_back),
                  onPressed: () => showDialog<bool>(
                    context: context,
                    builder: (c) => AlertDialog(
                      title: Text('Warning'),
                      content: Text('Are you sure you want to exit?'),
                      ),
                      actions: [
                        TextButton(
                            child: Text('Yes'),
                            onPressed: () {
                              Navigator.pop(c, true);
                              Navigator.pop(context);
                            }),
                        TextButton(
                          child: Text('No'),
                          onPressed: () => Navigator.pop(c, false),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
          

          【讨论】:

            【解决方案10】:

            只是一个简单的方法。用 WillPopScope 小部件包裹 Scaffold。

              WillPopScope(
                onWillPop: () async => false,
                child: Scaffold();
            

            【讨论】:

              【解决方案11】:

              如果您需要对系统后退按钮点击和应用栏后退按钮点击有不同的行为:您可以在调用Navigator.of(context).pop()之前删除onWillPop回调:

              @override
                Widget build(BuildContext context) {
                  return WillPopScope(
                    onWillPop: onWillPop,
                    child: Scaffold(
                      appBar: AppBar(
                        leading: IconButton(
                          onPressed: () {
                            ModalRoute.of(context)?.removeScopedWillPopCallback(onWillPop);
                            Navigator.of(context).pop();
                          },
                          icon: const Icon(Icons.arrow_back),
                        ),
                        title: Text(context.l10n.searchResults),
                      ),
                      body: MyBody(),
                    ),
                  );
                }
              

              在这种情况下,当用户点击系统返回按钮时,onWillPop 回调将决定是否弹出屏幕。但是当用户点击应用栏后退按钮时,屏幕会立即弹出。

              【讨论】:

                【解决方案12】:

                使用生成的路由,您可以通过这样做返回到目标页面-

                AppBar(
                        leading: new IconButton(
                        icon: new Icon(Icons.arrow_back),
                         onPressed: () {
                         Navigator.of(context) .pushNamedAndRemoveUntil(AppRouter.dashboard, (route) => false);
                         }),
                       )
                

                【讨论】:

                  猜你喜欢
                  • 2020-03-13
                  • 2023-01-27
                  • 2021-06-21
                  • 2019-07-10
                  • 1970-01-01
                  • 2015-08-15
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多