Flutter加载弹窗使用问题及解决方案

一、问题说明

在 APP 中,常见的做法是使用:

bash 复制代码
Navigator.push(
  context,
  PopupRoute(child: LoadingWidget()),
);

来实现加载弹窗(Loading Dialog)。
实际问题:

  1. 弹窗与 Navigator 栈绑定
  • Loading 弹窗占用栈顶,关闭时依赖 Navigator.pop()
  • 如果页面跳转或系统回收,栈状态可能被破坏
  1. 弹窗被误关闭或无法关闭
  • 其他弹窗(例如升级弹窗、确认框)调用 Navigator.pop() 时,可能把 Loading 弹窗直接关闭
  • _isShowing 或标识状态与栈状态不同步,导致 Loading 一直显示,用户看到"永久加载"
  1. 多请求同时显示 Loading 弹窗容易冲突
  • 多次 Navigator.push 会叠加在栈顶
  • pop 一个可能影响另一个

结论:
❌ Loading 弹窗不能依赖 Navigator 弹出,否则容易出现弹窗关闭异常、UI 卡死或误关闭其他弹窗的情况。


二、解决方案

1️⃣ 使用 Overlay 管理 Loading 弹窗

  • Overlay 是独立于 Navigator 栈的浮层
  • 可以随时显示或隐藏,不受页面跳转、其他 Dialog 或系统回收影响
  • 可以保证 Loading 弹窗独立、可靠、可复用
    2️⃣ 核心思路
  • 创建 OverlayEntry 来展示 Loading 弹窗
  • 显示时调用 Overlay.of(context).insert(entry)
  • 隐藏时调用 entry.remove()

三、示例代码

bash 复制代码
import 'package:audio_wave/audio_wave.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

///加载弹框(方法名不变,样式完全保留)
class CallStatusDialog {
  static OverlayEntry? _entry;

  ///展示
  static void showProgress(BuildContext context, String content) {
    if (_entry != null) return;

    _entry = OverlayEntry(
      builder: (_) => Stack(
        children: [
          const ModalBarrier(
            dismissible: false,
            color: Colors.black54,
          ),
          Center(
            child: new Padding(
              padding: const EdgeInsets.all(12.0),
              child: new Center(
                child: new SizedBox(
                  width: 120.0,
                  height: 120.0,
                  child: new Container(
                    decoration: ShapeDecoration(
                      color: Color(0xffffffff),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.all(
                          Radius.circular(8.0),
                        ),
                      ),
                    ),
                    child: new Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        AudioWave(
                          height: 32,
                          width: 50,
                          spacing: 2.5,
                          beatRate: Duration(milliseconds: 400),
                          bars: [
                            AudioWaveBar(heightFactor: 0.3, color: Colors.lightBlue.shade50),
                            AudioWaveBar(heightFactor: 0.6, color: Colors.lightBlue.shade100),
                            AudioWaveBar(heightFactor: 0.5, color: Colors.lightBlue.shade200),
                            AudioWaveBar(heightFactor: 0.8, color: Colors.lightBlue.shade300),
                            AudioWaveBar(heightFactor: 0.6, color: Colors.lightBlue.shade400),
                            AudioWaveBar(heightFactor: 1, color: Colors.lightBlue.shade500),
                            AudioWaveBar(heightFactor: 0.6, color: Colors.lightBlue.shade600),
                            AudioWaveBar(heightFactor: 0.4, color: Colors.lightBlue.shade700),
                            AudioWaveBar(heightFactor: 0.5, color: Colors.lightBlue.shade800),
                            AudioWaveBar(heightFactor: 0.2, color: Colors.lightBlue.shade900),

                          ],
                        ),
                        Container(
                          margin: EdgeInsets.only(top: 15),
                          child: Text(content,style: TextStyle(fontSize: 15,color: Colors.black87,fontFamily: 'PingFangBold', decoration: TextDecoration.none,),),
                        )
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );

    Overlay.of(context, rootOverlay: true)!.insert(_entry!);
  }

  ///隐藏
  static void hideProgress(BuildContext context) {
    _entry?.remove();
    _entry = null;
  }
}

四、使用方式

bash 复制代码
// 显示加载弹窗
CallStatusDialog.showProgress(context, "加载中...");

// 隐藏加载弹窗
CallStatusDialog.hideProgress(context);

特点:

  • 弹窗独立于 Navigator 栈,不会被其他页面或弹窗误关闭
  • 支持多次调用,只显示一个实例
  • UI 样式可完全自定义

五、总结

不要用 Navigator 弹出 Loading 弹窗,否则会出现:

  • 弹窗误关闭
  • 永久 loading
  • 页面跳转导致状态异常
  • 推荐使用 Overlay 管理 Loading 弹窗,独立于 Navigator 栈,稳定可靠,且样式可自定义
相关推荐
一直在想名20 小时前
Flutter 框架跨平台鸿蒙开发 - 黑白屏
flutter·华为·kotlin·harmonyos
AI_零食20 小时前
Flutter 框架跨平台鸿蒙开发 - 孤独指数应用
学习·flutter·开源·harmonyos
浮芷.21 小时前
Flutter 框架跨平台鸿蒙开发 - 儿童技能打卡墙应用
科技·flutter·华为·harmonyos·鸿蒙
Utopia^21 小时前
Flutter 框架跨平台鸿蒙开发 - 重力感知
flutter·华为·harmonyos
提子拌饭13321 小时前
昼夜节律下的肝脏代谢清除率演算仪:基于鸿蒙Flutter的双路流场与酶解粒子对照架构
flutter·华为·架构·harmonyos·鸿蒙
小雨天気.21 小时前
Flutter 框架跨平台鸿蒙开发 - 直觉训练器应用
flutter·华为·harmonyos
浮芷.21 小时前
Flutter 框架跨平台鸿蒙开发 - 姿势纠正助手应用
科技·flutter·华为·harmonyos·鸿蒙
一直在想名1 天前
Flutter 框架跨平台鸿蒙开发 - 影子收藏家
flutter·华为·harmonyos
早點睡3901 天前
Flutter for OpenHarmony三方库适配实战:palette_generator 图片调色板提取
flutter
2301_822703201 天前
开源鸿蒙跨平台Flutter开发:跨端图形渲染引擎的类型边界与命名空间陷阱:以多维雷达图绘制中的 dart:ui 及 StrokeJoin 异常为例
算法·flutter·ui·开源·图形渲染·harmonyos·鸿蒙