Flutter无按钮触发场景下showCupertinoDialog不执行如何解决
FTP下载失败iOS端自动弹窗异常问题
问题背景
FTP文件下载失败时需要弹出通知窗口,下载成功/失败状态通过bool类型变量返回,下载失败时返回false。
目前Android端使用标准showDialog方法可以正常弹窗,触发逻辑代码如下:
if(context.read<ScoreImageProvider>().bDownloadRes == false){ SchedulerBinding.instance!.addPostFrameCallback((_) { _ftpErrorAlert(context); }); }
iOS端使用标准showCupertinoDialog方法无法正常执行,当前编写的弹窗函数_ftpErrorAlert代码如下:
_ftpErrorAlert(BuildContext context){ Size size = MediaQuery.of(context).size; Platform.isAndroid ? showDialog( context: context, barrierDismissible: true, builder: (_) => AlertDialog( title: _ftpErrorTitle(size), content: _ftpErrorContent(size), actions: <Widget>[_ftpErrorOkButton(size)], ) ) : showCupertinoDialog( context: context, barrierDismissible: true, builder: (_) => CupertinoAlertDialog( title: _ftpErrorTitle(size), content: _ftpErrorContent(size), actions: <Widget>[_ftpErrorOkButton(size)], ) ); }
核心诉求:无需点击按钮触发的场景下,如何正确回调执行showCupertinoDialog完成弹窗展示。
故障原因
- 弹窗调用时未显式指定使用根Navigator,iOS端
showCupertinoDialog对context所属的路由层级校验比Android端showDialog更严格,当前页面的context如果处于路由过渡未完成、局部组件嵌套层级过深的状态,会找不到可用的Navigator挂载点导致弹窗不显示。 - 3.7版本以前的Flutter中,
showCupertinoDialog不支持barrierDismissible参数,传入该参数会直接抛出静默异常阻断弹窗流程,而showDialog很早就适配了该参数,所以Android端表现正常。 - 直接在build方法中做状态判断触发弹窗,build方法会在页面生命周期内多次执行,容易出现帧未渲染完成就调用弹窗、重复触发弹窗的问题。
修复方案
- 调整触发逻辑:不要在build方法中直接判断状态触发弹窗,将状态监听逻辑放到
initState中,触发前增加mounted判断防止页面销毁后执行异常。
@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final provider = context.read<ScoreImageProvider>(); provider.addListener(() { if (!mounted) return; if (provider.bDownloadRes == false) { _ftpErrorAlert(context); } }); }); }
- 修正弹窗函数:显式给两个弹窗方法加
useRootNavigator: true参数,指定使用根路由的Navigator挂载弹窗;如果使用的Flutter版本低于3.7,直接删除showCupertinoDialog的barrierDismissible参数,需要点击空白关闭的话自行给弹窗内容加手势识别即可。
_ftpErrorAlert(BuildContext context){ Size size = MediaQuery.of(context).size; if(Platform.isAndroid){ showDialog( context: context, barrierDismissible: true, useRootNavigator: true, builder: (_) => AlertDialog( title: _ftpErrorTitle(size), content: _ftpErrorContent(size), actions: <Widget>[_ftpErrorOkButton(size)], ) ); } else { showCupertinoDialog( context: context, useRootNavigator: true, // Flutter版本<3.7请删除下一行代码 barrierDismissible: true, builder: (_) => CupertinoAlertDialog( title: _ftpErrorTitle(size), content: _ftpErrorContent(size), actions: <Widget>[_ftpErrorOkButton(size)], ) ); } }
- 可选优化:如果使用Flutter 3.13及以上版本,可以直接用官方提供的
showAdaptiveDialog方法,会自动根据平台选择对应风格的弹窗,不需要手动判断平台,减少兼容代码量。
内容的提问来源于stack exchange,提问作者Jungwon
相关产品推荐
相关产品推荐

