Flutter:主内容加载后如何带淡出动画彻底移除Stack顶部加载屏
解决方案
要实现加载完成后平滑淡出并彻底移除LoadingBody,同时避免Google Map组件卡顿,核心思路是将加载组件的状态管理与Map组件解耦,只更新必要部分,具体实现如下:
1. 用ValueListenable解耦状态更新
首先将loadingStatus改为ValueNotifier类型,通过ValueListenableBuilder监听状态变化,仅触发加载组件的更新,不会导致整个父组件(包括Map)重建:
// 父组件中定义状态 final ValueNotifier<LoadingStatus> _loadingStatus = ValueNotifier(LoadingStatus.loading); // 当地图和数据就绪时,更新状态 void _onPageReady() { _loadingStatus.value = LoadingStatus.done; }
2. 封装独立的加载动画组件
创建独立的StatefulWidget负责加载动画的显示、淡出和最终移除,内部管理动画结束后的状态:
class LoadingOverlay extends StatefulWidget { final bool isLoading; final Color backgroundColor; const LoadingOverlay({ super.key, required this.isLoading, required this.backgroundColor, }); @override State<LoadingOverlay> createState() => _LoadingOverlayState(); } class _LoadingOverlayState extends State<LoadingOverlay> { bool _shouldRender = true; @override void didUpdateWidget(covariant LoadingOverlay oldWidget) { super.didUpdateWidget(oldWidget); // 从加载状态切换为完成状态时,延迟执行移除操作(等待动画结束) if (oldWidget.isLoading && !widget.isLoading) { Future.delayed(const Duration(milliseconds: 500), () { if (mounted) { setState(() => _shouldRender = false); } }); } } @override Widget build(BuildContext context) { // 彻底移除组件,不再占用资源 if (!_shouldRender) return const SizedBox.shrink(); return IgnorePointer( // 加载完成后忽略点击事件 ignoring: !widget.isLoading, child: AnimatedOpacity( duration: const Duration(milliseconds: 500), opacity: widget.isLoading ? 1 : 0, child: Container( color: widget.backgroundColor, child: Center( child: Lottie.asset( "assets/loading.json", // 加载完成后停止动画循环,节省资源 repeat: widget.isLoading, ), ), ), ), ); } }
3. 在Stack中整合组件
用ValueListenableBuilder包裹加载组件,确保只有加载部分随状态变化更新:
return Stack( children: [ // Google Map组件,不会因加载状态变化而重建 MapBody(), ValueListenableBuilder<LoadingStatus>( valueListenable: _loadingStatus, builder: (context, status, _) { return LoadingOverlay( isLoading: status == LoadingStatus.loading, backgroundColor: chargeDarkGrey, ); }, ), ], );
关键优化点
- 解耦状态更新:用
ValueNotifier和ValueListenableBuilder避免父组件整体重建,防止Map卡顿。 - 彻底移除组件:动画结束后将加载组件替换为
SizedBox.shrink(),完全释放其占用的资源。 - 停止动画循环:加载完成后关闭Lottie的重复播放,进一步降低资源消耗。
内容的提问来源于stack exchange,提问作者Emre Turan
相关产品推荐
相关产品推荐

