如何在Flutter中停用或覆盖Android“后退”按钮?


101

在特定页面上,有没有办法停用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秒钟,否则我将无法离开该屏幕。(我正在尝试为幼儿编写一个应用程序,希望只有父母能够导航到特定屏幕之外)。

Answers:


187

答案是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(),
        ),
      ),
    ),
  );
}

1
对于那些想拦截Form弹出窗口的人,使用Form的onWillPop属性更加方便。它可以访问表单状态,并且可以首先检查是否存在用户可能不想丢失的任何状态。
乔·拉普

嗨,@RémiRousselet,您能帮我在后面的堆栈上进行管理吗,就像我拥有一堆A,B,C,D屏幕一样,我想从D-> B或D-> A导航,那么我将如何管理它。你能指导我吗
Ravindra Kushwaha

我知道这个答案可能很旧。但是是创业板!考虑扩大解释。它的工作精湛。谢谢。
瓦尔

1
对于某人想在pop()之前做某事,可以使用它onWillPop: () async { onBack(); // "DO YOUR FUNCTION IS HERE WHEN POP" return false; }
休伊塔

20

正如RémiRousselet所指出的那样,WillPopScope通常是要走的路。但是,如果开发的状态小部件应直接对后退按钮做出反应,则可以使用以下方法:

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


完全尊重,您认为这更适合作为评论吗?
CopsOnRoad

20
@CopsOnRoad不,因为它不是对另一个答案的评论,而是解决同一问题的完全不同的方法。
MarcG

16

尽管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),
          ),
        ],
      ),
    ),
  );
}

为什么这个答案会被否决?有没有人实施这个并且有不好的经验?
SamiAzar

我想转到上一个屏幕,而不是按设备上的“后退”按钮后转到第一个屏幕。我不需要像这样的对话框。我该怎么办
LakshmiYogeshwaran

4

我将其发布在这里,以防有人发现并希望他们找到一个简单的示例 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"),
              )
          )
      ),
    );
  }
}

我昨天确实是用过它。请记住,Apple仅适用于Android,没有后退按钮。尽管您也许可以告诉我哪里出了问题,所以我可以修复它,或者它只能在我的特定模拟器上运行。我已经两周没有更新自己的颤动了,所以也许就是问题所在。让我知道
Bryan取消了

@BryanCancel仅当您没有任何推送路线时,您的答案才有效。请参见方法WidgetsBinding.handlePopRoute()。它以注册顺序通知观察者,并在收到真假后立即停止。当有推入的路线时,导航器首先返回true,然后您的观察者实际上从未被调用。换句话说,当用户单击“后退”按钮且没有更多路由时,您的代码仅可防止应用程序关闭。
MarcG

3

您可以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 => _allow
林恩

@Lynn是的,我知道。我只想分享Future.value()通话。
CopsOnRoad

3

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

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(),

2

答案也许您知道使用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


1

只需在应用栏的开头添加一个容器即可隐藏后退按钮。

AppBar(
        title: new Text('Page Title'),
        leading: new Container(),
      ),
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.