Flutter 自定义动画:创造独特的视觉体验

张开发
2026/4/7 19:39:18 15 分钟阅读

分享文章

Flutter 自定义动画:创造独特的视觉体验
Flutter 自定义动画创造独特的视觉体验突破内置动画的局限创造属于你自己的动画效果。一、自定义动画的魅力作为一名追求像素级还原的 UI 匠人我深知内置动画的局限。有时候设计稿上的那个特殊效果那个微妙的运动轨迹只有通过自定义动画才能完美实现。Flutter 提供了强大的动画系统让我们能够创造出独特的、符合品牌调性的动画效果。二、基础AnimationController 与 Tweenimport package:flutter/material.dart; class CustomAnimation extends StatefulWidget { override _CustomAnimationState createState() _CustomAnimationState(); } class _CustomAnimationState extends StateCustomAnimation with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _animation; override void initState() { super.initState(); _controller AnimationController( duration: Duration(seconds: 2), vsync: this, ); // 自定义 Tween _animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); _controller.repeat(reverse: true); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(自定义动画)), body: Center( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform( transform: Matrix4.identity() ..rotateZ(_animation.value * 2 * 3.14159) ..scale(0.5 _animation.value * 0.5), alignment: Alignment.center, child: Container( width: 200, height: 200, decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF667eea), Color(0xFF764ba2)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(20), ), ), ); }, ), ), ); } override void dispose() { _controller.dispose(); super.dispose(); } }三、自定义曲线1. 贝塞尔曲线class CustomCurve extends Curve { override double transform(double t) { // 自定义贝塞尔曲线 final p1 Offset(0, 0); final p2 Offset(0.2, 1.2); final p3 Offset(0.8, -0.2); final p4 Offset(1, 1); // 三次贝塞尔曲线 final tt t * t; final ttt tt * t; final u 1 - t; final uu u * u; final uuu uu * u; final x uuu * p1.dx 3 * uu * t * p2.dx 3 * u * tt * p3.dx ttt * p4.dx; final y uuu * p1.dy 3 * uu * t * p2.dy 3 * u * tt * p3.dy ttt * p4.dy; return y; } } // 使用自定义曲线 class CustomCurveAnimation extends StatefulWidget { override _CustomCurveAnimationState createState() _CustomCurveAnimationState(); } class _CustomCurveAnimationState extends StateCustomCurveAnimation with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _animation; override void initState() { super.initState(); _controller AnimationController( duration: Duration(seconds: 2), vsync: this, ); _animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: CustomCurve()), ); _controller.repeat(reverse: true); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(自定义曲线)), body: Center( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.translate( offset: Offset(0, _animation.value * 200 - 100), child: Container( width: 100, height: 100, decoration: BoxDecoration( color: Color(0xFF667eea), borderRadius: BorderRadius.circular(50), ), ), ); }, ), ), ); } override void dispose() { _controller.dispose(); super.dispose(); } }2. 弹性曲线class BounceCurve extends Curve { final double height; BounceCurve({this.height 1.0}); override double transform(double t) { if (t 0.5) { return 4 * height * t * t * t; } else { final ft 2 * t - 2; return height * (1 ft * ft * ft); } } } // 使用弹性曲线 class BounceAnimation extends StatefulWidget { override _BounceAnimationState createState() _BounceAnimationState(); } class _BounceAnimationState extends StateBounceAnimation with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _animation; override void initState() { super.initState(); _controller AnimationController( duration: Duration(seconds: 1), vsync: this, ); _animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: BounceCurve()), ); _controller.repeat(reverse: true); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(弹性动画)), body: Center( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.scale( scale: 0.5 _animation.value * 0.5, child: Container( width: 200, height: 200, decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF667eea), Color(0xFF764ba2)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), ), ), ); }, ), ), ); } override void dispose() { _controller.dispose(); super.dispose(); } }四、自定义 Tweenclass ColorTween extends TweenColor { ColorTween({required Color begin, required Color end}) : super(begin: begin, end: end); override Color lerp(double t) { final r (begin!.red (end!.red - begin!.red) * t).toInt(); final g (begin!.green (end!.green - begin!.green) * t).toInt(); final b (begin!.blue (end!.blue - begin!.blue) * t).toInt(); return Color.fromRGBO(r, g, b, 1.0); } } // 使用自定义 Tween class ColorAnimation extends StatefulWidget { override _ColorAnimationState createState() _ColorAnimationState(); } class _ColorAnimationState extends StateColorAnimation with SingleTickerProviderStateMixin { late AnimationController _controller; late AnimationColor _colorAnimation; override void initState() { super.initState(); _controller AnimationController( duration: Duration(seconds: 3), vsync: this, ); _colorAnimation ColorTween( begin: Color(0xFF667eea), end: Color(0xFFf093fb), ).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); _controller.repeat(reverse: true); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(颜色动画)), body: Center( child: AnimatedBuilder( animation: _colorAnimation, builder: (context, child) { return Container( width: 200, height: 200, decoration: BoxDecoration( color: _colorAnimation.value, borderRadius: BorderRadius.circular(12), ), ); }, ), ), ); } override void dispose() { _controller.dispose(); super.dispose(); } }五、物理动画class PhysicsAnimation extends StatefulWidget { override _PhysicsAnimationState createState() _PhysicsAnimationState(); } class _PhysicsAnimationState extends StatePhysicsAnimation with SingleTickerProviderStateMixin { late AnimationController _controller; late AnimationOffset _animation; override void initState() { super.initState(); _controller AnimationController( vsync: this, duration: Duration(seconds: 2), ); // 物理模拟 final spring SpringSimulation( SpringDescription( mass: 1.0, stiffness: 100.0, damping: 10.0, ), 0.0, 1.0, 0.0, ); _animation _controller.drive( TweenOffset( begin: Offset(-1, 0), end: Offset(0, 0), ), ); _controller.animateWith(spring); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(物理动画)), body: Center( child: SlideTransition( position: _animation, child: Container( width: 200, height: 200, decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF667eea), Color(0xFF764ba2)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), ), ), ), ), ); } override void dispose() { _controller.dispose(); super.dispose(); } }六、路径动画class PathAnimation extends StatefulWidget { override _PathAnimationState createState() _PathAnimationState(); } class _PathAnimationState extends StatePathAnimation with SingleTickerProviderStateMixin { late AnimationController _controller; late Animationdouble _animation; override void initState() { super.initState(); _controller AnimationController( duration: Duration(seconds: 3), vsync: this, ); _animation Tweendouble(begin: 0, end: 1).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); _controller.repeat(); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(路径动画)), body: Center( child: AnimatedBuilder( animation: _animation, builder: (context, child) { final path Path() ..moveTo(0, 0) ..quadraticBezierTo(150, -100, 300, 0) ..quadraticBezierTo(150, 100, 0, 0); final metrics path.computeMetrics().first; final position metrics.getTangentForOffset(metrics.length * _animation.value); return Transform.translate( offset: Offset(position!.position.dx - 50, position.position.dy), child: Container( width: 100, height: 100, decoration: BoxDecoration( color: Color(0xFF667eea), borderRadius: BorderRadius.circular(50), ), ), ); }, ), ), ); } override void dispose() { _controller.dispose(); super.dispose(); } }七、性能优化使用 const 构造器减少不必要的重建合理使用 AnimatedBuilder只重建需要动画的部分避免在动画回调中做耗时操作保持动画流畅使用 RepaintBoundary隔离重绘区域八、最佳实践封装动画逻辑将动画逻辑封装到自定义 widget 中使用 AnimationBuilder避免不必要的重建合理设置动画时长根据动画类型设置合适的时长测试性能在低端设备上测试动画流畅度自定义动画是创意的表达是界面的灵魂。#flutter #animation #custom-animation #dart #ui

更多文章