如何编写一个双后退按钮以使用Flutter退出应用程序

时间:2018-11-27 09:11:21

标签: dart flutter

我是扑朔迷离的新手,我看到双击后退按钮时,许多android应用程序都可以退出。

第一次按下后退按钮,应用程序显示吐司“再次按下以退出应用程序”。 在第二次按下时,应用程序退出。 当然,两次按下之间的时间不能太长。

如何扑通扑通?

9 个答案:

答案 0 :(得分:15)

这是我的代码示例(我用“ fluttertoast”作为昭通吐司-您可以使用小吃店或警报器或其他工具)

DateTime currentBackPressTime;

@override
Widget build(BuildContext context) {
  return Scaffold(
    ...
    body: WillPopScope(child: getBody(), onWillPop: onWillPop),
  );
}

Future<bool> onWillPop() {
    DateTime now = DateTime.now();
    if (currentBackPressTime == null || 
        now.difference(currentBackPressTime) > Duration(seconds: 2)) {
      currentBackPressTime = now;
      Fluttertoast.showToast(msg: exit_warning);
      return Future.value(false);
    }
    return Future.value(true);
  }

答案 1 :(得分:2)

这是我的答案。我使用AlertDialog()实现了这一点

 @override
  Widget build(BuildContext context) {
    return new WillPopScope(
      onWillPop: _onBackPressed,
      child: Scaffold(
        appBar: AppBar(),
        body: Container(),
      ),
    );
  }
  Future<bool> _onBackPressed() {
    return showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text('Confirm'),
          content: Text('Do you want to exit the App'),
          actions: <Widget>[
            FlatButton(
              child: Text('No'),
              onPressed: () {
                Navigator.of(context).pop(false); //Will not exit the App
              },
            ),
            FlatButton(
              child: Text('Yes'),
              onPressed: () {
                Navigator.of(context).pop(true); //Will exit the App
              },
            )
          ],
        );
      },
    ) ?? false;
  }

答案 2 :(得分:2)

不幸的是,它们都不适合我,我编写了一个通用类(小部件)来处理双击退出。如果有人感兴趣

class DoubleBackToCloseWidget extends StatefulWidget {
  final Widget child; // Make Sure this child has a Scaffold widget as parent.

  const DoubleBackToCloseWidget({
    @required this.child,
  });

  @override
  _DoubleBackToCloseWidgetState createState() =>
      _DoubleBackToCloseWidgetState();
}

class _DoubleBackToCloseWidgetState extends State<DoubleBackToCloseWidget> {
  int _lastTimeBackButtonWasTapped;
  static const exitTimeInMillis = 2000;

  bool get _isAndroid => Theme.of(context).platform == TargetPlatform.android;

  @override
  Widget build(BuildContext context) {
    if (_isAndroid) {
      return WillPopScope(
        onWillPop: _handleWillPop,
        child: widget.child,
      );
    } else {
      return widget.child;
    }
  }

  Future<bool> _handleWillPop() async {
    final _currentTime = DateTime.now().millisecondsSinceEpoch;

    if (_lastTimeBackButtonWasTapped != null &&
        (_currentTime - _lastTimeBackButtonWasTapped) < exitTimeInMillis) {
      Scaffold.of(context).removeCurrentSnackBar();
      return true;
    } else {
      _lastTimeBackButtonWasTapped = DateTime.now().millisecondsSinceEpoch;
      Scaffold.of(context).removeCurrentSnackBar();
      Scaffold.of(context).showSnackBar(
        _getExitSnackBar(context),
      );
      return false;
    }
  }

  SnackBar _getExitSnackBar(
    BuildContext context,
  ) {
    return SnackBar(
      content: Text(
        'Press BACK again to exit!',
        color: Colors.white,
      ),
      backgroundColor: Colors.red,
      duration: const Duration(
        seconds: 2,
      ),
      behavior: SnackBarBehavior.floating,
    );
  }
}

通过以下方式使用这个类:

class Dashboard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: DoubleBackToCloseWidget(
          child: Container(
            child: Column(
              children: [
                const Text('Hello there'),
                const Text('Hello there again'),
              ],
            ),
          ),
        ),
      ),
    );
  }
}


答案 3 :(得分:1)

只需使用double_back_to_close_app库

https://pub.dev/packages/double_back_to_close_app

在pubspec.yaml文件中的依赖项下添加double_back_to_close_app

dependencies:
  double_back_to_close_app: ^1.2.0

示例代码

import 'package:double_back_to_close_app/double_back_to_close_app.dart';
import 'package:flutter/material.dart';

void main() => runApp(Example());

class Example extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: DoubleBackToCloseApp(
          snackBar: const SnackBar(
            content: Text('Tap back again to leave'),
          ),
          child: Center(
            child: OutlineButton(
              child: const Text('Tap to simulate back'),
              // ignore: invalid_use_of_protected_member
              onPressed: WidgetsBinding.instance.handlePopRoute,
            ),
          ),
        ),
      ),
    );
  }
}

只需将您的身体内容移动到“ DoubleBackToCloseApp的”孩子

答案 4 :(得分:1)

如果您需要小吃车,则应提供与脚手架相关的脚手架钥匙,因此此钥匙应具有在其脚手架父级之外调用小吃车的技巧。

这是一个解决方案:

class Home extends StatelessWidget {

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
  return WillPopScope(
    onWillPop: () async{
        DateTime initTime = DateTime.now();
        popped +=1;
        if(popped>=2) return true;
        await _scaffoldKey.currentState.showSnackBar(
        SnackBar(
            behavior: SnackBarBehavior.floating,
            content: Text('Tap one more time to exit.',textAlign: TextAlign.center,),
            duration: Duration(seconds: 2),
        )).closed;

        // if timer is > 2 seconds reset popped counter
        if(DateTime.now().difference(initTime)>=Duration(seconds: 2)) {
        popped = 0;
        }
        return false;
        },
    child: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(title : Text("Demo")),
        body: Text("body")
    );
  )
}

答案 5 :(得分:1)

这是我的解决方案,您可以将backPressTotal值更改为所需的按下次数!

int backPressCounter = 0;
int backPressTotal = 2;

@override
Widget build(BuildContext context) {
  return Scaffold(
    ...
    body: WillPopScope(child: getBody(), onWillPop: onWillPop),
  );
}

Future<bool> onWillPop() {
    if (backPressCounter < 2) {
      Fluttertoast.showToast(msg: "Press ${backPressTotal - backPressCounter} time to exit app");
      backPressCounter++;
      Future.delayed(Duration(seconds: 1, milliseconds: 500), () {
        backPressCounter--;
      });
      return Future.value(false);
    } else {
      return Future.value(true);
    }
}

答案 6 :(得分:1)

如果条件是用户仅按两次,则当然可以使用第一种解决方案。 如果要增加单击次数,可以使用此解决方案。用户必须在两秒钟内按下3次才能退出游戏

  DateTime currentBackPressTime;
  /// init counter of clicks
  int pressCount=1;

然后:

Future<bool> onWillPop() async {

  DateTime now = DateTime.now();



/// here I check if number of clicks equal 3
if(pressCount!=3){

  ///should be assigned at the first click.
  if(pressCount ==1 )
    currentBackPressTime = now;
  pressCount+=1;


  return Future.value(false);
  }else{
  if (currentBackPressTime == null ||
      now.difference(currentBackPressTime) > Duration(seconds: 2)) {


    currentBackPressTime = now;
    pressCount=0;

  
    return Future.value(false);
  }
 }


 return Future.value(true);
}

答案 7 :(得分:0)

您也可以选择加入涉及SnackBar的解决方案。它不像Andrey Turkovsky的答案那么简单,但是却更加优雅,您不必依赖库。

使用Key

class _FooState extends State<Foo> {
  static const snackBarDuration = Duration(seconds: 3);

  final snackBar = SnackBar(
    content: Text('Press back again to leave'),
    duration: snackBarDuration,
  );

  final scaffoldKey = GlobalKey<ScaffoldState>();

  DateTime backButtonPressTime;

  @override
  Widget build(_) {
    return Scaffold(
      key: scaffoldKey,
      body: WillPopScope(
        onWillPop: onWillPop,
        child: Text('Place your child here'),
      ),
    );
  }

  Future<bool> onWillPop() async {
    DateTime currentTime = DateTime.now();

    bool backButtonHasNotBeenPressedOrSnackBarHasBeenClosed =
        backButtonPressTime == null ||
            currentTime.difference(backButtonPressTime) > snackBarDuration;

    if (backButtonHasNotBeenPressedOrSnackBarHasBeenClosed) {
      backButtonPressTime = currentTime;
      scaffoldKey.currentState.showSnackBar(snackBar);
      return false;
    }

    return true;
  }
}

没有Key

class _FooState extends State<Foo> {
  static const snackBarDuration = Duration(seconds: 3);

  final snackBar = SnackBar(
    content: Text('Press back again to leave'),
    duration: snackBarDuration,
  );

  DateTime backButtonPressTime;

  @override
  Widget build(_) {
    return Scaffold(
      body: Builder(
        builder: (BuildContext context) {
          // The BuildContext must be from one of the Scaffold's children.
          return WillPopScope(
            onWillPop: () => onWillPop(context),
            child: Text('Place your child here'),
          );
        },
      ),
    );
  }

  Future<bool> onWillPop(BuildContext context) async {
    DateTime currentTime = DateTime.now();

    bool backButtonHasNotBeenPressedOrSnackBarHasBeenClosed =
        backButtonPressTime == null ||
            currentTime.difference(backButtonPressTime) > snackBarDuration;

    if (backButtonHasNotBeenPressedOrSnackBarHasBeenClosed) {
      backButtonPressTime = currentTime;
      Scaffold.of(context).showSnackBar(snackBar);
      return false;
    }

    return true;
  }
}

答案 8 :(得分:0)

第一次按下后退按钮,应用程序将显示一个AlertDialog“按yes退出应用程序,然后按No退出应用程序”。 这是我的代码示例(我使用了“ AlertDialog”)

   @override
      Widget build(BuildContext context) {

        return new WillPopScope(
          onWillPop: _onBackPressed,
          child: DefaultTabController(
            initialIndex: _selectedIndex,
            length: choices.length,
            child: Scaffold(
              appBar: AppBar(

                ),
              ),

          ),
        );
      }
         Future<bool> _onBackPressed() {
        return showDialog(
          context: context,
          builder: (context) {
            return AlertDialog(
              title: Text('Are you sure?'),
              content: Text('Do you want to exit an App'),
              actions: <Widget>[
                FlatButton(
                  child: Text('No'),
                  onPressed: () {
                    Navigator.of(context).pop(false);
                  },
                ),
                FlatButton(
                  child: Text('Yes'),
                  onPressed: () {
                    Navigator.of(context).pop(true);
                  },
                )
              ],
            );
          },
        ) ?? false;
      }