Flutter 高级动画完全指南引言动画是提升用户体验的关键因素Flutter 提供了强大而灵活的动画系统。本文将深入探讨 Flutter 动画的高级特性包括自定义动画、复杂动画组合、性能优化等内容。动画基础回顾Flutter 中的动画主要分为两类补间动画Tween Animation在两个值之间平滑过渡物理动画Physics Animation模拟真实物理效果// 基础补间动画 AnimationController controller AnimationController( duration: Duration(seconds: 1), vsync: this, ); Animationdouble animation Tweendouble(begin: 0, end: 1).animate(controller);高级技巧一自定义 Tween创建自定义的补间动画可以实现更复杂的效果class ColorTween extends TweenColor { ColorTween({Color? begin, Color? end}) : super(begin: begin, end: end); override Color lerp(double t) { if (begin null || end null) return begin ?? end ?? Colors.transparent; return Color.fromARGB( lerpInt(begin!.alpha, end!.alpha, t), lerpInt(begin!.red, end!.red, t), lerpInt(begin!.green, end!.green, t), lerpInt(begin!.blue, end!.blue, t), ); } } // 使用自定义 Tween AnimationColor colorAnimation ColorTween( begin: Colors.blue, end: Colors.red, ).animate(controller);高级技巧二动画曲线与缓动函数Flutter 提供了丰富的动画曲线// 使用内置曲线 Animationdouble animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation( parent: controller, curve: Curves.easeInOut, ), ); // 自定义曲线 class CustomCurve extends Curve { override double transform(double t) { // 实现自定义缓动逻辑 return t * t * (3 - 2 * t); // 经典的 ease-out cubic } } // 组合曲线 Animationdouble combinedAnimation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation( parent: controller, curve: Interval(0.2, 0.8, curve: Curves.bounceOut), ), );高级技巧三动画控制器进阶重复动画controller.repeat( min: 0, max: 1, reverse: true, period: Duration(seconds: 2), );动画监听animation.addListener(() { setState(() { // 更新 UI }); }); animation.addStatusListener((status) { if (status AnimationStatus.completed) { controller.reverse(); } else if (status AnimationStatus.dismissed) { controller.forward(); } });高级技巧四交错动画交错动画是指多个动画按顺序或重叠执行class StaggerAnimation extends StatelessWidget { final Animationdouble controller; late final Animationdouble opacity; late final Animationdouble width; late final Animationdouble height; late final AnimationEdgeInsets padding; StaggerAnimation({required this.controller}) { opacity Tweendouble(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: controller, curve: Interval(0.0, 0.100, curve: Curves.easeIn), ), ); width Tweendouble(begin: 50.0, end: 200.0).animate( CurvedAnimation( parent: controller, curve: Interval(0.125, 0.250, curve: Curves.easeIn), ), ); height Tweendouble(begin: 50.0, end: 200.0).animate( CurvedAnimation( parent: controller, curve: Interval(0.250, 0.375, curve: Curves.easeIn), ), ); padding EdgeInsetsTween( begin: const EdgeInsets.only(bottom: 16.0), end: const EdgeInsets.only(bottom: 75.0), ).animate( CurvedAnimation( parent: controller, curve: Interval(0.250, 0.375, curve: Curves.easeIn), ), ); } Widget _buildAnimation(BuildContext context, Widget? child) { return Container( padding: padding.value, alignment: Alignment.bottomCenter, child: Opacity( opacity: opacity.value, child: Container( width: width.value, height: height.value, color: Colors.blue, ), ), ); } override Widget build(BuildContext context) { return AnimatedBuilder( animation: controller, builder: _buildAnimation, ); } }高级技巧五Hero 动画进阶Hero 动画用于页面间的元素过渡// 源页面 Hero( tag: imageHero, child: Image.network(https://example.com/image.jpg), ) // 目标页面 Hero( tag: imageHero, child: Image.network(https://example.com/image.jpg), ) // 自定义 Hero 动画 class CustomHero extends StatelessWidget { final Widget child; final String tag; const CustomHero({super.key, required this.child, required this.tag}); override Widget build(BuildContext context) { return Hero( tag: tag, createRectTween: (begin, end) { return CustomRectTween(begin: begin!, end: end!); }, child: child, ); } } class CustomRectTween extends RectTween { CustomRectTween({required super.begin, required super.end}); override Rect lerp(double t) { final curvedT Curves.easeInOut.transform(t); return Rect.lerp(begin, end, curvedT)!; } }高级技巧六物理动画物理动画模拟真实世界的物理效果// 弹簧动画 AnimationController springController AnimationController( vsync: this, duration: const Duration(seconds: 2), ); SpringSimulation spring SpringSimulation( SpringDescription( mass: 1.0, stiffness: 100.0, damping: 10.0, ), 0.0, // 起始值 1.0, // 结束值 0.0, // 初始速度 ); Animationdouble springAnimation springController.drive(spring); // 重力动画 GravitySimulation gravity GravitySimulation( 9.8, // 重力加速度 0.0, // 起始位置 100.0, // 结束位置 0.0, // 初始速度 );高级技巧七自定义绘制动画使用CustomPainter创建复杂的绘制动画class AnimatedCirclePainter extends CustomPainter { final Animationdouble animation; AnimatedCirclePainter({required this.animation}) : super(repaint: animation); override void paint(Canvas canvas, Size size) { final center Offset(size.width / 2, size.height / 2); final radius animation.value * min(size.width, size.height) / 2; Paint paint Paint() ..color Colors.blue ..style PaintingStyle.fill; canvas.drawCircle(center, radius, paint); // 添加发光效果 Paint glowPaint Paint() ..color Colors.blue.withOpacity(0.3) ..style PaintingStyle.stroke ..strokeWidth 4; canvas.drawCircle(center, radius 10 * animation.value, glowPaint); } override bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } } // 使用 AnimatedBuilder( animation: animation, builder: (context, child) { return CustomPaint( painter: AnimatedCirclePainter(animation: animation), size: Size(200, 200), ); }, )高级技巧八动画性能优化1. 使用RepaintBoundaryRepaintBoundary( child: AnimatedWidget(...), )2. 使用const构造函数// 避免不必要的重建 const Text(Hello, style: TextStyle(fontSize: 16));3. 使用AnimatedBuilderAnimatedBuilder( animation: animation, builder: (context, child) { return Transform.scale( scale: animation.value, child: child, // child 不会被重建 ); }, child: const Icon(Icons.star), )4. 避免在build方法中创建对象// 错误做法 Widget build(BuildContext context) { final animation Tweendouble(begin: 0, end: 1).animate(controller); return ...; } // 正确做法 late final Animationdouble animation; override void initState() { super.initState(); animation Tweendouble(begin: 0, end: 1).animate(controller); }实战案例复杂动画组件class FloatingButton extends StatefulWidget { const FloatingButton({super.key}); override StateFloatingButton createState() _FloatingButtonState(); } class _FloatingButtonState extends StateFloatingButton with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _scaleAnimation; late Animationdouble _opacityAnimation; late AnimationOffset _slideAnimation; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _scaleAnimation Tweendouble(begin: 0.8, end: 1.0).animate( CurvedAnimation(parent: _controller, curve: Curves.easeOutBack), ); _opacityAnimation Tweendouble(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _controller, curve: Curves.easeIn), ); _slideAnimation TweenOffset( begin: const Offset(0, 20), end: Offset.zero, ).animate( CurvedAnimation(parent: _controller, curve: Curves.easeOut), ); // 延迟启动动画 Future.delayed(const Duration(milliseconds: 500), () { _controller.forward(); }); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return SlideTransition( position: _slideAnimation, child: FadeTransition( opacity: _opacityAnimation, child: ScaleTransition( scale: _scaleAnimation, child: FloatingActionButton( onPressed: () { _controller.reverse().then((_) { // 按钮点击后的逻辑 }); }, child: const Icon(Icons.add), ), ), ), ); } }实战案例粒子效果class Particle { double x; double y; double vx; double vy; double radius; Color color; Particle({ required this.x, required this.y, required this.vx, required this.vy, required this.radius, required this.color, }); } class ParticleSystem extends StatefulWidget { const ParticleSystem({super.key}); override StateParticleSystem createState() _ParticleSystemState(); } class _ParticleSystemState extends StateParticleSystem with SingleTickerProviderStateMixin { late AnimationController _controller; late ListParticle particles; override void initState() { super.initState(); _controller AnimationController( duration: const Duration(milliseconds: 16), vsync: this, )..repeat(); _initParticles(); _controller.addListener(() { _updateParticles(); }); } void _initParticles() { particles List.generate(50, (index) { return Particle( x: 200 Random().nextDouble() * 200, y: 200 Random().nextDouble() * 200, vx: (Random().nextDouble() - 0.5) * 2, vy: (Random().nextDouble() - 0.5) * 2, radius: Random().nextDouble() * 5 2, color: Colors.blue.withOpacity(Random().nextDouble() * 0.5 0.5), ); }); } void _updateParticles() { setState(() { for (var particle in particles) { particle.x particle.vx; particle.y particle.vy; // 边界检测 if (particle.x particle.radius || particle.x 400 - particle.radius) { particle.vx * -1; } if (particle.y particle.radius || particle.y 400 - particle.radius) { particle.vy * -1; } } }); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return CustomPaint( size: const Size(400, 400), painter: ParticlePainter(particles: particles), ); } } class ParticlePainter extends CustomPainter { final ListParticle particles; ParticlePainter({required this.particles}); override void paint(Canvas canvas, Size size) { for (var particle in particles) { Paint paint Paint()..color particle.color; canvas.drawCircle(Offset(particle.x, particle.y), particle.radius, paint); } } override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }常见问题与解决方案Q1动画卡顿怎么办A检查是否有以下问题避免在动画中进行复杂计算使用RepaintBoundary隔离重绘区域考虑使用Transform代替PositionedQ2如何实现循环动画A使用controller.repeat()方法controller.repeat(reverse: true);Q3如何同步多个动画A使用同一个AnimationController驱动多个 Tweenfinal opacity Tweendouble(begin: 0, end: 1).animate(controller); final scale Tweendouble(begin: 0.5, end: 1).animate(controller);总结Flutter 的动画系统非常强大通过本文的学习你应该能够创建自定义 Tween 和曲线实现复杂的交错动画使用物理模拟创建真实感动画利用 CustomPainter 绘制自定义动画优化动画性能动画是提升应用体验的关键不断练习和尝试新的动画效果让你的应用更加生动有趣。