You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Flutter自定义加载屏幕指示器集成问题排查:加载动画在新项目正常但现有项目无显示

问题分析与解决方案

你遇到的核心问题是:直接在异步函数里调用Loader()并不会把这个加载动画渲染到UI上。Flutter的Widget必须被添加到Widget树中才能显示,而你当前的写法只是创建了一个Widget实例,但没有任何逻辑把它挂载到当前界面里——这和你之前用的UIUtils.showProcessIndicator()不一样,旧工具类应该是封装了Dialog或Overlay的逻辑来显示加载框。

下面给你两种可行的修复方案,你可以根据项目情况选择:


方案1:用Dialog封装加载动画(最接近你旧代码的用法)

首先把你的Loader抽成一个独立的Widget:

class LoaderWidget extends StatelessWidget {
  const LoaderWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 130,
        width: 135,
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(17),
          border: Border.all(color: Colors.blue)
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.end,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: const [
            Text(
              "Loading...",
              style: TextStyle(
                fontSize: 13,
                letterSpacing: 2.1,
                color: Colors.blue,
              ),
            ),
            Padding(
              padding: EdgeInsets.all(3.0),
              child: Image.network(
                "https://cdn.dribbble.com/users/42716/screenshots/3913441/media/4ef7d67070fee7ab75948280f51d369f.gif",
                height: 100,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

然后修改你的_vReport()函数,用showDialog来显示这个加载动画,并在API请求完成后关闭:

Future<List<VeReportModel>>? _vReport() async {
  debugPrint("Do not exploit this code $fDate &&&&&&&&& Thank you");
  // 定义Dialog的上下文,用于后续关闭
  late BuildContext dialogContext;
  try {
    if (await NetworkUtils.isNetworkAvailable()) {
      // 显示加载Dialog,禁止点击空白关闭
      showDialog(
        context: context,
        barrierDismissible: false,
        builder: (ctx) {
          dialogContext = ctx;
          return const LoaderWidget();
        },
      );

      ApiResponse? apiResponse = await RestApiClient(session: session)
          .vReport(fDate, tDate, spedLimit, stLimit, vId!);
      
      // 请求完成后关闭加载Dialog
      Navigator.of(dialogContext).pop();

      Common.printWrapped('Map: _vEvent(): apiResponse=$apiResponse');
      if (apiResponse!.successful && apiResponse.code == apiSuccessCode) {
        if (apiResponse.data != null) {
          List mapMessages = apiResponse.result;
          debugPrint("mapMessage $mapMessages");
          return mapMessages
              .map((v) => VeReportModel.fromJson(v))
              .toList();
        }
      }
    } else {
      UIUtils.displayInternetError(context);
    }
  } catch (e,stack) {
    // 异常时也要确保关闭Dialog,避免加载框残留
    if (dialogContext.mounted) {
      Navigator.of(dialogContext).pop();
    }
    UIUtils.catchErrorMsg(context);
    debugPrint('Error While acknowledging v report : $e');
    GeneralException.handleError(e, stack: stack, module: moduleVeReport);
    debugPrint('VeReport: Error while getting v list: $e');
  }
  return [];
}

方案2:在页面状态中控制加载动画(更优雅的UI集成)

如果你的页面是StatefulWidget,可以通过状态变量来控制加载动画的显示:

  1. 在页面State里添加状态变量:
bool _isLoading = false;
  1. 修改数据请求函数,更新加载状态:
Future<void> _fetchVReport() async {
  setState(() {
    _isLoading = true;
  });
  try {
    if (await NetworkUtils.isNetworkAvailable()) {
      ApiResponse? apiResponse = await RestApiClient(session: session)
          .vReport(fDate, tDate, spedLimit, stLimit, vId!);
      
      Common.printWrapped('Map: _vEvent(): apiResponse=$apiResponse');
      if (apiResponse!.successful && apiResponse.code == apiSuccessCode) {
        if (apiResponse.data != null) {
          List<VeReportModel> reportList = apiResponse.result
              .map((v) => VeReportModel.fromJson(v))
              .toList();
          // 处理获取到的数据,比如更新页面列表
        }
      }
    } else {
      UIUtils.displayInternetError(context);
    }
  } catch (e,stack) {
    UIUtils.catchErrorMsg(context);
    debugPrint('Error While acknowledging v report : $e');
    GeneralException.handleError(e, stack: stack, module: moduleVeReport);
    debugPrint('VeReport: Error while getting v list: $e');
  } finally {
    // 无论请求成功或失败,都关闭加载状态
    setState(() {
      _isLoading = false;
    });
  }
}
  1. 在页面的build方法中,根据_isLoading条件渲染加载动画:
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: _isLoading 
        ? const LoaderWidget() // 显示加载动画
        : // 你的正常页面内容,比如列表、表单等
  );
}

关键注意点

  • 方案1中要确保在**所有代码分支(成功、失败、无网络)**都关闭Dialog,避免加载框一直停留在界面上;
  • 方案2适合需要把加载动画嵌入页面布局的场景,比如覆盖在列表上方,视觉体验更流畅;
  • 如果你用了Provider、Riverpod等状态管理库,可以把_isLoading放到全局状态中,逻辑类似。

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

火山引擎 最新活动