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

Flutter pop关闭圆形进度指示器误关ListView的解决方案

问题成因
  • 核心问题是操作的Navigator实例和弹窗所属的Navigator实例不匹配。Flutter中Navigator.of(context)会沿着组件树向上查找离传入context最近的Navigator实例作为操作对象:你调用showDialog弹出加载框时,该方法默认useRootNavigator: true,会把加载弹窗压入应用根Navigator(MaterialApp提供的顶层导航)的栈顶;但你调用pop关闭弹窗时传入的context来自ListView内部的表单项,如果你的自定义表单项FieldBuilderForm或页面内其他组件嵌套了独立的子Navigator,此时Navigator.of(context)拿到的是这个子Navigator的实例,调用pop只会操作子Navigator的路由栈,根本碰不到根Navigator上的加载弹窗,反而会把子Navigator栈顶的表单路由弹出,表现出来就是加载框没关,ListView组件被异常移除。
  • 存在时序隐患:你调用buildShowDialog时没有加await等待弹窗完成入栈,如果网络请求响应速度极快,会出现弹窗还没被压入导航栈就执行pop,误关闭栈内其他正常页面的问题。
  • 代码写法不严谨:弹出弹窗时没有显式声明导航栈归属,关闭时也没有指定要操作的Navigator实例,完全依赖context自动查找,在组件嵌套较深的页面很容易出现上下文匹配错误。
修复方案

按以下步骤修改即可解决问题:

  1. 改造加载弹窗方法,显式指定弹窗压入根导航栈,避免弹窗归属错误
  2. 调用弹窗方法时加await等待弹窗入栈完成,再发起网络请求,避免时序问题
  3. 关闭加载弹窗时显式指定操作根Navigator,确保找到的是加载框所属的导航实例
  4. 后续所有弹窗(包括错误提示弹窗)都显式配置useRootNavigator: true,统一弹窗的导航栈归属

修改后的核心代码如下:

// 改造后的加载弹窗方法
Future buildShowDialog(BuildContext context) {
  return showDialog(
    context: context,
    barrierDismissible: false,
    // 显式指定弹窗压入根导航栈
    useRootNavigator: true,
    builder: (BuildContext dialogContext) {
      return Center(
        child: LoadingAnimationWidget.threeArchedCircle(
            color: Colors.white, size: 50),
        );
    },
  );
}

loginFunction(http.Client client, jsonBody) async {
  // 等待加载弹窗完成入栈再执行后续逻辑
  await buildShowDialog(context);

  var url = Uri.parse("https://my-office-timesheet.herokuapp.com/api/signin");
  var respsonse = await client.post(url, body: jsonBody);

  if (respsonse.statusCode == 200) {
    // 显式指定操作根导航栈关闭加载框,避免找错子Navigator
    Navigator.of(context, rootNavigator: true).pop();
    if (this.mounted) {
      setState(() {
        jsonResponse = Post.fromJson(json.decode(respsonse.body));
      });
    }
    if (jsonResponse!.success == true) {
      if (this.mounted) {
        Navigator.of(context).pushAndRemoveUntil(
            MaterialPageRoute(builder: (BuildContext context) => Dashboard()),
            (Route<dynamic> route) => false);
      }
    } else {
      showDialog(
        context: context,
        // 错误提示弹窗也指定压入根导航栈
        useRootNavigator: true,
        builder: (ctx) => AlertDialog(
          title: Text("Message:"),
          content: Text("${jsonResponse?.message}"),
          actions: [
            ElevatedButton(
              onPressed: () {
                // 这里用弹窗自身的ctx关闭,不会出现匹配错误
                Navigator.of(ctx).pop();
                if (this.mounted) {
                  setState(() {
                    jsonResponse = null;
                  });
                }
              },
              child: const Text("Ok"),
            ),
          ],
        ),
      );
    }
  }

  return respsonse;
}

可选优化

如果需要更稳妥的弹窗控制,可以在弹出加载框时缓存弹窗自身的context,关闭时直接用缓存的context操作,完全避免外部context带来的匹配问题:

// 新增页面级变量缓存加载框上下文
BuildContext? _loadingContext;

Future buildShowDialog(BuildContext context) {
  return showDialog(
    context: context,
    barrierDismissible: false,
    useRootNavigator: true,
    builder: (BuildContext dialogContext) {
      _loadingContext = dialogContext;
      return Center(
        child: LoadingAnimationWidget.threeArchedCircle(
            color: Colors.white, size: 50),
      );
    },
  );
}

// 关闭加载框时使用缓存的context
if (_loadingContext != null && _loadingContext!.mounted) {
  Navigator.of(_loadingContext!).pop();
  _loadingContext = null;
}

内容的提问来源于stack exchange,提问作者Jeryn Immanuel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 01:21:32