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 栈,稳定可靠,且样式可自定义
相关推荐
SoaringHeart5 小时前
Flutter调试组件:打印任意组件尺寸位置信息 NRenderBox
前端·flutter
九狼10 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
_squirrel12 小时前
记录一次 Flutter 升级遇到的问题
flutter
Haha_bj13 小时前
Flutter——状态管理 Provider 详解
flutter·app
MakeZero16 小时前
Flutter那些事-展示型组件篇
flutter
赤心Online16 小时前
从零开始掌握 Shorebird:Flutter 热更新实战指南
flutter
wangruofeng17 小时前
AI 助力 Flutter 3.27 升级到 3.38 完整指南:两周踩坑与实战复盘
flutter·ios·ai编程
Zsnoin能1 天前
Flutter仿ios液态玻璃效果
flutter
傅里叶2 天前
iOS相机权限获取
flutter·ios
Haha_bj2 天前
Flutter—— 本地存储(shared_preferences)
flutter