Widget添加与移除时的缩放动画实现咨询
Flutter Stack组件中Widget的添加/移除缩放动画实现
核心实现思路
通过给Stack中的每个Widget包裹ScaleTransition,结合AnimationController单独控制每个项的缩放动画:
- 添加Widget时:初始化动画控制器,执行从0到1的缩放过渡,实现"从无到有"的效果
- 移除Widget时:先执行从1到0的缩放过渡,动画完成后再将Widget从Stack的子列表中移除
完整代码示例
import 'package:flutter/material.dart'; class AnimatedStackDemo extends StatefulWidget { const AnimatedStackDemo({super.key}); @override State<AnimatedStackDemo> createState() => _AnimatedStackDemoState(); } class _AnimatedStackDemoState extends State<AnimatedStackDemo> with TickerProviderStateMixin { // 维护Stack中的子项,每个项包含Widget和对应的动画控制器 final List<_AnimatedStackItem> _stackItems = []; int _itemCounter = 0; // 添加新Widget到Stack void _addItem() { setState(() { final controller = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); final scaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: controller, curve: Curves.easeOut), ); _stackItems.add(_AnimatedStackItem( key: UniqueKey(), child: Container( key: ValueKey(_itemCounter), width: 100, height: 100, color: Colors.primaries[_itemCounter % Colors.primaries.length], child: Center(child: Text('Item ${_itemCounter + 1}')), ), controller: controller, scaleAnimation: scaleAnimation, )); _itemCounter++; // 启动放大动画 controller.forward(); }); } // 移除最后一个Widget void _removeItem() { if (_stackItems.isEmpty) return; final lastItem = _stackItems.last; // 启动缩小动画,完成后移除并释放控制器 lastItem.controller.reverse().then((_) { setState(() { _stackItems.remove(lastItem); lastItem.controller.dispose(); }); }); } @override void dispose() { // 释放所有剩余的动画控制器 for (final item in _stackItems) { item.controller.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Animated Stack Demo')), body: Stack( alignment: Alignment.center, children: _stackItems.map((item) { return ScaleTransition( key: item.key, scale: item.scaleAnimation, child: item.child, ); }).toList(), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: _addItem, child: const Icon(Icons.add), ), const SizedBox(width: 10), FloatingActionButton( onPressed: _removeItem, child: const Icon(Icons.remove), ), ], ), ); } } // 封装Stack项的信息 class _AnimatedStackItem { final Key key; final Widget child; final AnimationController controller; final Animation<double> scaleAnimation; _AnimatedStackItem({ required this.key, required this.child, required this.controller, required this.scaleAnimation, }); }
关键细节说明
- 动画控制器管理:每个Stack项对应独立的
AnimationController,确保添加/移除动画互不干扰,动画结束后及时释放控制器避免内存泄漏 - 缩放动画曲线:示例中使用
Curves.easeOut让放大动画更自然,你可以根据需求替换为其他曲线(如Curves.bounceOut) - Stack布局:示例中用
Alignment.center让所有项居中叠加,你可以根据需要调整每个项的位置(比如给每个Container添加Positioned组件) - 唯一Key:给每个
ScaleTransition和子Widget设置唯一Key,帮助Flutter识别Widget的添加/移除动作,避免动画异常
内容的提问来源于stack exchange,提问作者Moel
相关产品推荐
相关产品推荐

