You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 10:01:46