
Flutter动画高级技巧1. 核心概念1.1 动画基础Animation动画对象管理动画的状态和值AnimationController控制动画的播放、暂停、反转等Tween定义动画的开始和结束值Curve动画曲线控制动画的缓动效果AnimatedBuilder用于构建动画UI1.2 动画类型补间动画从一个值过渡到另一个值物理动画模拟物理效果的动画交错动画多个动画按顺序执行页面转场动画页面之间的过渡效果Hero动画元素在页面之间的过渡效果2. 高级技巧2.1 补间动画class TweenAnimationExample extends StatefulWidget { override _TweenAnimationExampleState createState() _TweenAnimationExampleState(); } class _TweenAnimationExampleState extends StateTweenAnimationExample with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _animation; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(seconds: 2), vsync: this, ); _animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), )..addStatusListener((status) { if (status AnimationStatus.completed) { _controller.reverse(); } else if (status AnimationStatus.dismissed) { _controller.forward(); } }); _controller.forward(); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.scale( scale: 1 _animation.value * 0.5, child: Container( width: 200, height: 200, decoration: BoxDecoration( color: Colors.blue.withOpacity(0.7 _animation.value * 0.3), borderRadius: BorderRadius.circular(20 * _animation.value), ), ), ); }, ); } }2.2 物理动画class PhysicsAnimationExample extends StatefulWidget { override _PhysicsAnimationExampleState createState() _PhysicsAnimationExampleState(); } class _PhysicsAnimationExampleState extends StatePhysicsAnimationExample with SingleTickerProviderStateMixin { late AnimationController _controller; late AnimationOffset _animation; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(seconds: 2), vsync: this, ); final spring SpringSimulation( SpringDescription( mass: 1, stiffness: 100, damping: 10, ), 0, // 初始位置 1, // 目标位置 0, // 初始速度 ); _animation TweenOffset( begin: Offset(0, 0), end: Offset(1, 0), ).animate( _controller.drive(CurveTween(curve: Curves.linear)), ); _controller.animateWith(spring); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.translate( offset: _animation.value * 200, child: Container( width: 100, height: 100, color: Colors.red, ), ); }, ); } }2.3 交错动画class StaggeredAnimationExample extends StatefulWidget { override _StaggeredAnimationExampleState createState() _StaggeredAnimationExampleState(); } class _StaggeredAnimationExampleState extends StateStaggeredAnimationExample with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _opacityAnimation; late Animationdouble _scaleAnimation; late AnimationOffset _translateAnimation; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(seconds: 2), vsync: this, ); _opacityAnimation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: Interval(0, 0.5)), ); _scaleAnimation Tweendouble(begin: 0.5, end: 1).animate( CurvedAnimation(parent: _controller, curve: Interval(0.3, 0.7)), ); _translateAnimation TweenOffset(begin: Offset(0, 50), end: Offset(0, 0)).animate( CurvedAnimation(parent: _controller, curve: Interval(0.5, 1.0)), ); _controller.forward(); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, builder: (context, child) { return Opacity( opacity: _opacityAnimation.value, child: Transform.scale( scale: _scaleAnimation.value, child: Transform.translate( offset: _translateAnimation.value, child: Container( width: 200, height: 200, color: Colors.green, ), ), ), ); }, ); } }2.4 页面转场动画class PageTransitionExample extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(页面转场动画)), body: Center( child: ElevatedButton( onPressed: () { Navigator.push( context, PageRouteBuilder( transitionDuration: Duration(milliseconds: 500), pageBuilder: (context, animation, secondaryAnimation) SecondPage(), transitionsBuilder: (context, animation, secondaryAnimation, child) { const begin Offset(1.0, 0.0); const end Offset.zero; const curve Curves.easeInOut; var tween Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); return SlideTransition( position: animation.drive(tween), child: child, ); }, ), ); }, child: Text(跳转到第二页), ), ), ); } } class SecondPage extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(第二页)), body: Center( child: ElevatedButton( onPressed: () { Navigator.pop(context); }, child: Text(返回), ), ), ); } }2.5 Hero动画class HeroAnimationExample extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(Hero动画)), body: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, ), itemCount: 6, itemBuilder: (context, index) { return GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) DetailPage(index), ), ); }, child: Hero( tag: image_$index, child: Container( width: 150, height: 150, color: Colors.blue[index % 9 * 100], child: Center( child: Text( Image $index, style: TextStyle(color: Colors.white, fontSize: 18), ), ), ), ), ); }, ), ); } } class DetailPage extends StatelessWidget { final int index; DetailPage(this.index); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(详情页)), body: Center( child: Hero( tag: image_$index, child: Container( width: 300, height: 300, color: Colors.blue[index % 9 * 100], child: Center( child: Text( Image $index, style: TextStyle(color: Colors.white, fontSize: 24), ), ), ), ), ), ); } }2.6 自定义动画曲线class CustomCurve extends Curve { override double transform(double t) { // 自定义曲线逻辑 return sin(t * pi) * 0.5 0.5; } } class CustomCurveExample extends StatefulWidget { override _CustomCurveExampleState createState() _CustomCurveExampleState(); } class _CustomCurveExampleState extends StateCustomCurveExample with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _animation; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(seconds: 2), vsync: this, ); _animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: CustomCurve()), ); _controller.repeat(reverse: true); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.rotate( angle: _animation.value * 2 * pi, child: Container( width: 100, height: 100, color: Colors.purple, ), ); }, ); } }3. 最佳实践3.1 性能优化使用const构造器减少不必要的重建避免在build方法中创建动画将动画初始化放在initState使用RepaintBoundary减少重绘区域合理使用动画控制器及时dispose避免内存泄漏使用硬件加速启用Flutter的硬件加速3.2 动画设计保持动画简洁避免过度复杂的动画使用合适的曲线根据场景选择合适的动画曲线控制动画时长根据动画类型设置合理的时长保持一致性在应用中保持动画风格一致3.3 可访问性提供动画开关允许用户关闭动画考虑动画速度避免过快的动画影响用户体验确保动画不会导致闪烁避免频繁的重绘3.4 测试测试动画性能确保动画流畅测试不同设备确保在不同设备上的表现一致测试动画边界情况确保动画在各种情况下都能正常工作4. 实际应用4.1 登录按钮动画class LoginButton extends StatefulWidget { final VoidCallback onPressed; const LoginButton({Key? key, required this.onPressed}) : super(key: key); override _LoginButtonState createState() _LoginButtonState(); } class _LoginButtonState extends StateLoginButton with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _scaleAnimation; late Animationdouble _opacityAnimation; bool _isLoading false; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _scaleAnimation Tweendouble(begin: 1, end: 0.9).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); _opacityAnimation Tweendouble(begin: 1, end: 0).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); } override void dispose() { _controller.dispose(); super.dispose(); } void _handlePress() { setState(() { _isLoading true; _controller.forward(); }); // 模拟网络请求 Future.delayed(const Duration(seconds: 2), () { setState(() { _isLoading false; _controller.reverse(); }); widget.onPressed(); }); } override Widget build(BuildContext context) { return GestureDetector( onTap: _isLoading ? null : _handlePress, child: AnimatedBuilder( animation: _controller, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( width: 200, height: 50, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(25), ), child: Stack( alignment: Alignment.center, children: [ Opacity( opacity: _opacityAnimation.value, child: const Text( 登录, style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), if (_isLoading) const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimationColor(Colors.white), strokeWidth: 2, ), ), ], ), ), ); }, ), ); } }4.2 列表项动画class AnimatedListItem extends StatelessWidget { final Widget child; final int index; const AnimatedListItem({Key? key, required this.child, required this.index}) : super(key: key); override Widget build(BuildContext context) { return FadeTransition( opacity: AnimationController( duration: const Duration(milliseconds: 300), vsync: context.findAncestorStateOfTypeSingleTickerProviderStateMixin()!, )..forward(), child: SlideTransition( position: TweenOffset( begin: const Offset(0, 0.5), end: Offset.zero, ).animate( CurvedAnimation( parent: AnimationController( duration: const Duration(milliseconds: 300), vsync: context.findAncestorStateOfTypeSingleTickerProviderStateMixin()!, )..forward(), curve: Interval(0, 1, curve: Curves.easeOut), ), ), child: child, ), ); } } class AnimatedListExample extends StatelessWidget { final ListString items List.generate(20, (index) Item $index); override Widget build(BuildContext context) { return ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return AnimatedListItem( index: index, child: ListTile( title: Text(items[index]), leading: CircleAvatar( child: Text(${index 1}), ), ), ); }, ); } }5. 总结Flutter动画的高级技巧包括补间动画和物理动画交错动画和页面转场动画Hero动画和自定义动画曲线性能优化和动画设计可访问性和测试通过掌握这些技巧你可以创建出更加流畅、美观的Flutter应用动画提升用户体验和应用的视觉效果。