Flutter PopScope组件异常:返回键触发后出现白屏而非确认弹窗
Flutter PopScope触发系统返回手势时白屏问题排查与解决
问题场景
使用PopScope实现Android返回键/滑动返回手势触发离开确认弹窗,但实际操作时:
- 点击页面内的"Page Two"按钮,能正常弹出确认对话框
- 触发系统返回键或滑动返回手势时,页面出现白屏,无法正常显示弹窗
完整复现代码
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( initialRoute: '/home', onGenerateRoute: (RouteSettings settings) { return switch (settings.name) { '/two' => MaterialPageRoute( builder: (BuildContext context) => const PageTwo(), ), _ => MaterialPageRoute( builder: (BuildContext context) => const PageOne(), ), }; }, ); } } class PageOne extends StatelessWidget { const PageOne({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: TextButton( child: const Text('Page One'), onPressed: () { Navigator.of(context).pushNamed('/two'); }, ), ), ); } } class PageTwo extends StatefulWidget { const PageTwo({super.key}); @override State<PageTwo> createState() => _PageTwoState(); } class _PageTwoState extends State<PageTwo> { Future<bool?> _showBackDialog(BuildContext context) { return showDialog<bool>( context: context, builder: (BuildContext context) => AlertDialog( title: const Text('Are you sure?'), content: const Text( 'Are you sure you want to leave this page?', ), actions: <Widget>[ TextButton( child: const Text('Never Mind'), onPressed: () { Navigator.pop(context, false); }, ), TextButton( child: const Text('leave'), onPressed: () { Navigator.pop(context, true); }, ) ], ), ); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: PopScope( canPop: false, onPopInvoked: (bool didPop) async { if (didPop) { return; } final bool shouldPop = await _showBackDialog(context) ?? false; if (context.mounted && shouldPop) { Navigator.pop(context); } }, child: TextButton( child: const Text('Page Two'), onPressed: () async { final bool shouldPop = await _showBackDialog(context) ?? false; if (context.mounted && shouldPop) { Navigator.pop(context); } }, ), ), ), ); } }
问题原因
- 上下文层级冲突:原代码中
PopScope被嵌套在Scaffold的body内部,它的上下文并非当前路由的根上下文。当系统返回手势触发时,onPopInvoked回调使用当前页面的上下文弹出对话框,此时导航栈正处于处理返回操作的状态,上下文层级冲突导致对话框无法正确挂载,最终出现白屏。 - 导航状态混乱:
canPop: false阻止了系统默认的路由弹出,但在回调中使用非根上下文处理弹窗和路由弹出,会导致导航栈状态异常,进而引发页面渲染问题。
解决方案
方案一:调整PopScope的层级
将PopScope移到Scaffold的外层,使其成为当前路由的直接子Widget,确保它使用路由的根上下文处理返回事件:
@override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvoked: (bool didPop) async { if (didPop) { return; } final bool shouldPop = await _showBackDialog(context) ?? false; if (context.mounted && shouldPop) { Navigator.pop(context); } }, child: Scaffold( body: Center( child: TextButton( child: const Text('Page Two'), onPressed: () async { final bool shouldPop = await _showBackDialog(context) ?? false; if (context.mounted && shouldPop) { Navigator.pop(context); } }, ), ), ), ); }
方案二:使用根导航器弹出对话框
在showDialog中设置useRootNavigator: true,确保对话框挂载在根导航器上,避免上下文冲突:
Future<bool?> _showBackDialog(BuildContext context) { return showDialog<bool>( context: context, useRootNavigator: true, // 新增此行 builder: (BuildContext context) => AlertDialog( title: const Text('Are you sure?'), content: const Text( 'Are you sure you want to leave this page?', ), actions: <Widget>[ TextButton( child: const Text('Never Mind'), onPressed: () { Navigator.pop(context, false); }, ), TextButton( child: const Text('leave'), onPressed: () { Navigator.pop(context, true); }, ) ], ), ); }
两种方案都能解决白屏问题,推荐方案一,因为它更符合PopScope的使用规范,让返回事件的处理层级更清晰。
内容的提问来源于stack exchange,提问作者DData Science
相关产品推荐
相关产品推荐

