Flutter中WillPopScope结合showDialog不生效问题排查
问题描述
点击返回按钮时,预期弹出确认退出的AlertDialog,但点击后无弹窗显示,调试控制台仅输出pop in,未输出pop out。即使将WebView替换为简单Text组件,问题依然存在。
用户提供的代码:
class _MyAppState extends State<MyApp> { // This widget is the root of your application. WebViewController? _webViewController; final Completer<WebViewController> _completerController = Completer<WebViewController>(); @override void initState() { super.initState(); if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); } @override Widget build(BuildContext context) { return MaterialApp( home: WillPopScope( onWillPop: () async { print("pop in"); final shouldPop = await showDialog<bool>( context: context, builder: (context) { return AlertDialog( title: const Text('Do you want to go back?'), actionsAlignment: MainAxisAlignment.spaceBetween, actions: [ TextButton( onPressed: () { Navigator.pop(context, true); }, child: const Text('Yes'), ), TextButton( onPressed: () { Navigator.pop(context, false); }, child: const Text('No'), ), ], ); }, ); print("pop out"); return shouldPop!; }, child: Scaffold( body: SafeArea( child: WebView( onWebViewCreated: (WebViewController webViewController) { _completerController.future .then((value) => _webViewController = value); _completerController.complete(webViewController); }, initialUrl: 'https://www.google.com', javascriptMode: JavascriptMode.unrestricted, zoomEnabled: false, ), ), ), ), ); } }
问题原因
核心问题是context层级错误:MaterialApp内部会创建应用的根Navigator,而你在onWillPop中使用的context是_MyAppState的context,这个context属于MaterialApp的同级/父级,并未关联到MaterialApp内部的Navigator。导致showDialog无法找到正确的导航器来弹出对话框,代码卡在await showDialog步骤,因此不会执行到print("pop out")。
解决方案
以下两种方法任选其一即可:
方法一:使用Builder获取正确的context
通过Builder组件获取MaterialApp内部的context,再传入showDialog:
@override Widget build(BuildContext context) { return MaterialApp( home: Builder( builder: (innerContext) { return WillPopScope( onWillPop: () async { print("pop in"); final shouldPop = await showDialog<bool>( context: innerContext, // 使用Builder提供的内部context builder: (context) { return AlertDialog( title: const Text('Do you want to go back?'), actionsAlignment: MainAxisAlignment.spaceBetween, actions: [ TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Yes'), ), TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('No'), ), ], ); }, ); print("pop out"); return shouldPop ?? false; // 处理空值,默认不退出 }, child: Scaffold( body: SafeArea( child: WebView( onWebViewCreated: (WebViewController webViewController) { _completerController.future .then((value) => _webViewController = value); _completerController.complete(webViewController); }, initialUrl: 'https://www.google.com', javascriptMode: JavascriptMode.unrestricted, zoomEnabled: false, ), ), ), ); }, ), ); }
方法二:强制使用根Navigator弹窗
在showDialog中添加useRootNavigator: true参数,直接调用根导航器:
final shouldPop = await showDialog<bool>( context: context, useRootNavigator: true, // 强制使用根Navigator builder: (context) { return AlertDialog( title: const Text('Do you want to go back?'), actionsAlignment: MainAxisAlignment.spaceBetween, actions: [ TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Yes'), ), TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('No'), ), ], ); }, ); return shouldPop ?? false; // 替换原有的shouldPop!,避免空指针
补充说明
- 替换
shouldPop!为shouldPop ?? false是为了处理用户点击弹窗外部关闭的情况(此时shouldPop为null),避免触发空指针异常。 - 两种方案都能解决弹窗不显示的问题,根据代码结构选择即可。
内容的提问来源于stack exchange,提问作者Genie
相关产品推荐
相关产品推荐

