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

Flutter中Lottie动画首次加载延迟问题咨询(JSON仅13KB)

Flutter Lottie首次加载延迟问题排查与解决

问题结论

这不是正常现象,核心原因是首次加载时Lottie资源未预加载,导致动画初始化滞后于文本渲染;后续加载时资源已被缓存,所以显示正常。你的代码存在资源加载逻辑缺失和内存泄漏隐患,需要针对性优化。

原因分析

  1. Lottie首次从本地asset加载JSON(或压缩包)时,需完成文件读取、JSON解析、动画资源初始化等异步/耗时操作,而文本是同步渲染的,因此文本会先显示。
  2. 后续加载时,Lottie已将资源缓存到内存,无需重复初始化,所以动画能立即显示。
  3. 代码中使用Timer.periodic但未在组件销毁时取消,存在内存泄漏风险。

解决方案

1. 预加载Lottie资源

在组件初始化阶段提前加载Lottie资源,确保动画渲染时资源已就绪:

class _AnimatedLoadingCommonWidgetState extends State<AnimatedLoadingCommonWidget> {
  ValueNotifier<String> currentMessage = ValueNotifier('');
  late LottieComposition _lottieComposition;
  Timer? _messageTimer; // 新增Timer变量用于后续取消

  @override
  void initState() {
    super.initState();
    assert(widget.loadingMessages != null || widget.singleMessage != null);
    
    // 预加载Lottie资源
    _preloadLottie();
    
    if (widget.loadingMessages != null) {
      int currentMessageIndex = 1;
      currentMessage.value = widget.loadingMessages![0];
      
      // 保存Timer实例
      _messageTimer = Timer.periodic(const Duration(seconds: 2, milliseconds: 200), (_) {
        currentMessage.value = widget.loadingMessages![currentMessageIndex];
        currentMessageIndex = (currentMessageIndex + 1) % widget.loadingMessages!.length;
      });
    }
  }

  // 预加载Lottie资源的异步方法
  Future<void> _preloadLottie() async {
    _lottieComposition = await AssetLottie('assets/animations/json.zip').load();
    if (mounted) setState(() {});
  }

  @override
  void dispose() {
    _messageTimer?.cancel(); // 取消Timer,避免内存泄漏
    currentMessage.dispose(); // 释放ValueNotifier资源
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          widget.shouldAnimationAdapts
              ? Expanded(
                  child: Stack(
                    children: [
                      // 使用预加载的composition渲染动画
                      Lottie.composition(
                        composition: _lottieComposition,
                        animate: true,
                        frameRate: FrameRate.max,
                      ),
                      Image.asset('assets/images/shape.png'),
                    ],
                  ),
                )
              : Stack(
                  children: [
                    Lottie.composition(
                      composition: _lottieComposition,
                      animate: true,
                      frameRate: FrameRate.max,
                    ),
                    Image.asset('assets/images/shape.png'),
                  ],
                ),
          // 原有的文本区域代码保持不变
          SizedBox(
            height: 65,
            child: Column(
              children: [
                if (widget.loadingMessages != null)
                  ValueListenableBuilder(
                    valueListenable: currentMessage,
                    builder: (context, message, child) => Text(
                      message,
                      textAlign: TextAlign.center,
                      style: const TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w500,
                        color: kPrimaryColor,
                      ),
                    ),
                  ),
                if (widget.singleMessage != null)
                  RichText(
                    text: TextSpan(
                      style: const TextStyle(
                        fontSize: 12,
                        color: kPrimaryColor,
                        fontFamily: 'Poppins',
                      ),
                      children: widget.singleMessage,
                    ),
                  ),
                const SizedBox(
                  height: 8,
                ),
                const Text(
                  'Por favor, aguarde',
                  style: TextStyle(
                    fontSize: 10,
                    fontWeight: FontWeight.w400,
                    color: greyScale07,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

2. 优化资源格式

  • 直接使用JSON文件而非ZIP压缩包:ZIP体积虽小,但解压过程会增加首次加载耗时,建议将json.zip解压后直接使用loading.json,并修改asset路径。
  • 确保pubspec.yaml中asset配置正确,路径层级简洁:
    assets:
      - assets/animations/loading.json
      - assets/images/shape.png
    

3. 添加加载占位符(可选)

如果希望在预加载期间避免空白,可以添加尺寸匹配的占位符,提升用户体验:

// 将Stack中的Lottie组件替换为:
_lottieComposition == null
    ? const SizedBox(width: 200, height: 200, child: CircularProgressIndicator(color: kPrimaryColor))
    : Lottie.composition(
        composition: _lottieComposition,
        animate: true,
        frameRate: FrameRate.max,
      )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 10:15:41