Flutter中实现双击返回键退出APP

it2023-05-11  72

先来认识几个小知识点

WillPopScope

flutter官方提供的widget,注册一个异步函数,用于捕获并处理用户的返回键操作(系统提供的返回操作和Navigator.of(context).maybePop()),可以允许或禁止本次操作。我一般是包在Scaffold外面

WillPopScope( onWillPop : () async =>{ // 一些其他操作 return true; } child : Scaffold( body : Container() ); )

Navigator

这个不用过多赘述了,大家用到路由应该都用过,这次我们要用到其中一个静态方法:canPop,这个方法判断路由是否还能返回。

看到这,相信你已经有头绪了,尝试自己试试先敲一下?

流程分析

WillPopScope捕获用户操作Navigator.of(context).canPop()判断是否可以返回,如果可以,返回true,正常执行,如果不能,返回false阻止返回后,记录本次返回,根据与下次返回的时间关系来判断是否要退出APP

代码

// 退出APP标记 bool isQuit = false; // 清除标记定时器 Timer quitTimer; // 其他代码... Widget build(BuildContext context){ return WillPopScope( // 捕获用户操作,判断能否返回 onWillPop: () async { // 可以返回 if(!Navigator.canPop(context)){ // 检查退出标记 if(isQuit){ // 退出APP await SystemChannels.platform.invokeMethod('SystemNavigator.pop'); }else{ // 我包装的提示工具类,提示用户再次点击退出,使用的fluttertoast插件 ToastUtils.showToast(TipsTextConfig.quitApp); // 记录退出标记 isQuit = true; // 开启定时器,两秒后清除标记 quitTimer = Timer(Duration(seconds: 2) , (){ isQuit = false; quitTimer.cancel(); }); } // 阻止返回 return false; } // 默认正常运行 return true; }, child : Scaffold( body : Container(), ) ) }

没什么用的小技巧

WillPopScope不止可以用在Scaffold上,比如在showDialog中使用,可以阻止用户通过返回键关闭dialog窗口 showDialog( context : context , child : WillPopScope( onWillPop : () async => true , child : SimpleDialog(), ) ) 可以自己包装一个CustomScaffold,这样可以做一些自己的设置,而且可以公用双击退出APP的代码 /// 包装Scaffold /// 添加了安全区 /// 添加了防止元素大小随手机字体大小设置改变 class CustomScaffold extends StatelessWidget { final bool extendBody; final bool extendBodyBehindAppBar; final PreferredSizeWidget appBar; final Widget body; final Widget floatingActionButton; final FloatingActionButtonLocation floatingActionButtonLocation; final FloatingActionButtonAnimator floatingActionButtonAnimator; final List<Widget> persistentFooterButtons; final Widget drawer; final Widget endDrawer; final Color drawerScrimColor; final Color backgroundColor; final Widget bottomNavigationBar; final Widget bottomSheet; final bool resizeToAvoidBottomPadding; final bool resizeToAvoidBottomInset; final bool primary; final DragStartBehavior drawerDragStartBehavior; final double drawerEdgeDragWidth; final bool drawerEnableOpenDragGesture; final bool endDrawerEnableOpenDragGesture; final bool safeArea; CustomScaffold({ Key key, this.appBar, this.body, this.floatingActionButton, this.floatingActionButtonLocation, this.floatingActionButtonAnimator, this.persistentFooterButtons, this.drawer, this.endDrawer, this.bottomNavigationBar, this.bottomSheet, this.backgroundColor, this.resizeToAvoidBottomPadding, this.resizeToAvoidBottomInset, this.primary = true, this.drawerDragStartBehavior = DragStartBehavior.start, this.extendBody = false, this.extendBodyBehindAppBar = false, this.drawerScrimColor, this.drawerEdgeDragWidth, this.drawerEnableOpenDragGesture = true, this.endDrawerEnableOpenDragGesture = true, this.safeArea = true }) : super(key: key); @override Widget build(BuildContext context) { // 退出APP标记 bool isQuit = false; Timer quitTimer; return MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: WillPopScope( onWillPop: () async { if(!Navigator.canPop(context)){ if(isQuit){ await SystemChannels.platform.invokeMethod('SystemNavigator.pop'); }else{ ToastUtils.showToast(TipsTextConfig.quitApp); isQuit = true; quitTimer = Timer(Duration(seconds: 2) , (){ isQuit = false; quitTimer.cancel(); }); } return false; } return true; }, child: Scaffold( appBar: this.appBar, body: SafeArea( top: safeArea, bottom: safeArea, left: safeArea, right: safeArea, child: this.body, ), floatingActionButton: this.floatingActionButton, floatingActionButtonLocation: this.floatingActionButtonLocation, floatingActionButtonAnimator: this.floatingActionButtonAnimator, persistentFooterButtons: this.persistentFooterButtons, drawer: this.drawer, endDrawer: this.endDrawer, bottomNavigationBar: this.bottomNavigationBar, bottomSheet: this.bottomSheet, backgroundColor: this.backgroundColor, resizeToAvoidBottomPadding: this.resizeToAvoidBottomPadding, resizeToAvoidBottomInset: this.resizeToAvoidBottomInset, primary: this.primary, drawerDragStartBehavior: this.drawerDragStartBehavior, extendBody: this.extendBody, extendBodyBehindAppBar: this.extendBodyBehindAppBar, drawerScrimColor: this.drawerScrimColor, drawerEdgeDragWidth: this.drawerEdgeDragWidth, drawerEnableOpenDragGesture: this.drawerEnableOpenDragGesture, endDrawerEnableOpenDragGesture: this.endDrawerEnableOpenDragGesture, ), ), ); } }
最新回复(0)