Flutter中Lottie动画首次加载延迟问题咨询(JSON仅13KB)
Flutter Lottie首次加载延迟问题排查与解决
问题结论
这不是正常现象,核心原因是首次加载时Lottie资源未预加载,导致动画初始化滞后于文本渲染;后续加载时资源已被缓存,所以显示正常。你的代码存在资源加载逻辑缺失和内存泄漏隐患,需要针对性优化。
原因分析
- Lottie首次从本地asset加载JSON(或压缩包)时,需完成文件读取、JSON解析、动画资源初始化等异步/耗时操作,而文本是同步渲染的,因此文本会先显示。
- 后续加载时,Lottie已将资源缓存到内存,无需重复初始化,所以动画能立即显示。
- 代码中使用
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
相关产品推荐
相关产品推荐

