Dart 如何使用flatter编写按下双返回按钮退出应用程序

Dart 如何使用flatter编写按下双返回按钮退出应用程序,dart,flutter,Dart,Flutter,我是个新手,我看到很多android应用程序在双击后退按钮时都可以退出 第一次按“后退”按钮时,应用程序将显示一个祝酒词“再次按可退出应用程序”。 第二次按下后,应用程序退出。 当然,两次按压之间的时间不能太长 如何在flatter中执行此操作?这是我的代码示例(我使用了“flattertoast”来显示toast消息,您可以使用snackbar或alert或其他任何工具) DateTime currentBackPressTime; @凌驾 小部件构建(构建上下文){ 返回脚手架( ... b

我是个新手,我看到很多android应用程序在双击后退按钮时都可以退出


第一次按“后退”按钮时,应用程序将显示一个祝酒词“再次按可退出应用程序”。 第二次按下后,应用程序退出。 当然,两次按压之间的时间不能太长


如何在flatter中执行此操作?

这是我的代码示例(我使用了“flattertoast”来显示toast消息,您可以使用snackbar或alert或其他任何工具)

DateTime currentBackPressTime;
@凌驾
小部件构建(构建上下文){
返回脚手架(
...
body:WillPopScope(子项:getBody(),onWillPop:onWillPop),
);
}
未来的onWillPop(){
DateTime now=DateTime.now();
如果(currentBackPressTime==null | |
现在.差异(currentBackPressTime)>持续时间(秒:2)){
currentBackPressTime=现在;
flattertoast.showtoos(消息:退出警告);
返回Future.value(false);
}
返回未来值(true);
}
您可以试试这个软件包

在包装所有小部件的
Scaffold
内,放置
DoubleBackToCloseApp
并传递一个SnackBar:

类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
家:脚手架(
正文:DoubleBackToCloseApp(
孩子:家(),
snackBar:const snackBar(
内容:文本(“再次点击返回以离开”),
),
),
),
);
}
}
旧答案 您还可以选择包含
SnackBar
的解决方案。这并不像你的答案那么简单,但它更优雅,而且你不会依赖于图书馆

class\u FooState扩展状态{
静态常数snackBarDuration=持续时间(秒:3);
最终蛇杆=蛇杆(
内容:文本(“再次按后退以离开”),
持续时间:snackBarDuration,
);
DateTime BackButton按时间;
@凌驾
小部件构建(构建上下文){
返回脚手架(
//BuildContext必须来自脚手架的一个子项。
车身:建造商(
生成器:(上下文){
返回式示波器(
onWillPop:()=>handleWillPop(上下文),
孩子:文本(“将孩子放在这里”),
);
},
),
);
}
未来handleWillPop(BuildContext上下文)异步{
final now=DateTime.now();
最后一个BackButton按钮未按下NackBar按钮已关闭=
backButtonPressTime==null||
现在。差异(backButtonPressTime)>snackBarDuration;
如果(BackButton未按下NackBar已关闭){
backButtonPressTime=现在;
脚手架.of(上下文).showSnackBar(snackBar);
返回false;
}
返回true;
}
}

第一次按后退按钮时,应用程序显示一个警报对话框“按是退出应用程序,按否无法退出应用程序”。 这是我的代码示例(我使用了“AlertDialog”)

@覆盖
小部件构建(构建上下文){
返回新的Willposcope(
onWillPop:_onBackPressed,
子级:DefaultTabController(
初始索引:\ u选择索引,
长度:选择。长度,
孩子:脚手架(
appBar:appBar(
),
),
),
);
}
未来_onBackPressed(){
返回显示对话框(
上下文:上下文,
生成器:(上下文){
返回警报对话框(
标题:文本(“你确定吗?”),
内容:文本(“是否要退出应用程序”),
行动:[
扁平按钮(
child:Text('No'),
已按下:(){
Navigator.of(context.pop)(false);
},
),
扁平按钮(
子项:文本('Yes'),
已按下:(){
Navigator.of(context.pop)(true);
},
)
],
);
},
)??假;
}

这是我的答案。我使用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;
  }
@覆盖
小部件构建(构建上下文){
返回新的Willposcope(
onWillPop:_onBackPressed,
孩子:脚手架(
appBar:appBar(),
主体:容器(),
),
);
}
未来_onBackPressed(){
返回显示对话框(
上下文:上下文,
生成器:(上下文){
返回警报对话框(
标题:文本(“确认”),
内容:文本(“是否要退出应用程序”),
行动:[
扁平按钮(
child:Text('No'),
已按下:(){
Navigator.of(context.pop(false);//将不会退出应用程序
},
),
扁平按钮(
子项:文本('Yes'),
已按下:(){
Navigator.of(context.pop(true);//将退出应用程序
},
)
],
);
},
)??假;
}

只需使用double\u back\u关闭应用程序库即可

将double_back_添加到pubspec.yaml文件中依赖项下的_close_应用程序中

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”的子项

如果你想要一个snackbar,你应该提供一个脚手架键,因为它与脚手架相关,所以这个键应该能够在它的脚手架父项之外调用snackbar

以下是一个解决方案:

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

class Home扩展了无状态小部件{
最终GlobalKey _scaffoldKey=新的GlobalKey();
@凌驾
小部件构建(构建上下文){
返回式示波器(
onWillPop:()异步{
DateTime initTime=DateTime.now();
弹出+=1;
如果(弹出>=2)返回true;
等待_scaffoldKey.currentState.showSnackBar(
小吃条(
行为:SnackBarBehavior.floating,
内容:Text('再点击一次退出',textAlign:textAlign.center,),
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,
            ),
          ),
        ),
      ),
    );
  }
}
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")
    );
  )
}

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