先来认识几个小知识点
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,
),
),
);
}
}