如何修复showDialog关闭整个页面而非仅弹窗的问题?
问题描述
调用Cubit的emit(Waiting)时,会弹出一个带全屏黑色背景和居中加载指示器的showDialog;当API返回200时emit(Success),调用Navigator.of(context, rootNavigator: true).pop()本应关闭弹窗,却导致整个主组件被关闭,回到登录页面。
具体场景:登录进入主组件后,点击TabBar的Home标签(或在HomeWidget的initState中调用BlocProvider.of<StatisticsCubit>(context).callApi()),API响应成功后,弹窗消失的同时主组件也被关闭,回到登录页。
主组件代码
class _MainWidgetState extends State<MainWidget> with WindowListener { onTap: (index) { // 点击Home标签时也会触发问题,直接回到登录页 if(index == 0) { BlocProvider.of<StatisticsCubit>(context).callApi(); } } return TabBar( tabs: [ Tab( child: "Home", ), Tab( child: "Test", ), Tab( child: "Statistics", ) ] ) }
HomeWidget代码
class _HomeWidgetState extends State<HomeWidget> with WindowListener { @override void initState() { BlocProvider.of<StatisticsCubit>(context).callApi(); super.initState(); } @override Widget build(BuildContext context) { return BlocConsumer<StatisticsCubit, StatisticsState>( listener: (context,state) { if (state is WaitingNfb) { showDialog( context: context, builder: (context) => Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, color: Colors.black.withOpacity(0.7), child: const Center( child: CircularProgressIndicator( strokeWidth: 1, color: Colors.black, backgroundColor: Colors.white, ), ), ) ); } if (state is Success) { Navigator.of(context, rootNavigator: true).pop(); // 这里会直接回到登录页,而不是只关闭弹窗 } }, builder: (context, state) { return Container( child: , ) }), ) } }
解决方案
问题核心是rootNavigator: true会操作全局根导航器,而你的登录页正好在根导航栈的最底层,所以pop会直接回到登录页。以下是两种精准关闭弹窗的方法:
方法一:保存弹窗专属上下文,精准关闭
打开弹窗时记录弹窗自身的上下文,后续仅用这个上下文关闭弹窗,避免影响页面导航栈:
class _HomeWidgetState extends State<HomeWidget> with WindowListener { // 新增变量保存弹窗上下文 BuildContext? _dialogContext; @override void initState() { BlocProvider.of<StatisticsCubit>(context).callApi(); super.initState(); } @override Widget build(BuildContext context) { return BlocConsumer<StatisticsCubit, StatisticsState>( listener: (context,state) { if (state is WaitingNfb) { // 弹窗打开时保存其专属上下文 showDialog( context: context, builder: (dialogContext) { _dialogContext = dialogContext; return Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, color: Colors.black.withOpacity(0.7), child: const Center( child: CircularProgressIndicator( strokeWidth: 1, color: Colors.black, backgroundColor: Colors.white, ), ), ); } ); } if (state is Success) { // 仅用弹窗上下文关闭弹窗 if (_dialogContext != null) { Navigator.of(_dialogContext!).pop(); _dialogContext = null; // 清空避免重复调用 } } }, builder: (context, state) { return Container(); }), ); } }
方法二:移除rootNavigator,使用当前上下文关闭
如果你的弹窗是挂载在当前页面的导航器下,直接去掉rootNavigator: true,用当前上下文关闭即可:
if (state is Success) { Navigator.of(context).pop(); // 移除rootNavigator参数 }
额外优化:避免重复弹窗
如果多次触发Waiting状态会打开多个弹窗,可以在打开前判断是否已有弹窗存在:
if (state is WaitingNfb) { if (_dialogContext == null) { // 仅在无弹窗时打开 showDialog(...); } }
内容的提问来源于stack exchange,提问作者newbiras
相关产品推荐
相关产品推荐

