引言

Flutter凭借其强大的动画系统,让开发者能够创造出媲美原生应用的流畅动画效果。从简单的过渡动画到复杂的粒子系统,Flutter提供了丰富而灵活的动画API。本文将深入探讨Flutter动画的高级技巧,帮助你打造电影级的用户体验。

Flutter动画系统架构

动画核心概念

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Flutter动画系统架构
/*
AnimationController → Animation → Tween → Widget
     ↓                ↓           ↓         ↓
   控制时间线          数值变化      插值计算    视觉更新
*/

// 基础动画控制器
import 'package:flutter/material.dart';

class BasicAnimationDemo extends StatefulWidget {
  @override
  _BasicAnimationDemoState createState() => _BasicAnimationDemoState();
}

class _BasicAnimationDemoState extends State<BasicAnimationDemo>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  late Animation<Color?> _colorAnimation;
  late Animation<Size?> _sizeAnimation;

  @override
  void initState() {
    super.initState();

    // 创建动画控制器
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );

    // 创建补间动画
    _animation = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    ));

    // 颜色动画
    _colorAnimation = ColorTween(
      begin: Colors.blue,
      end: Colors.red,
    ).animate(_controller);

    // 尺寸动画
    _sizeAnimation = SizeTween(
      begin: const Size(100, 100),
      end: const Size(200, 200),
    ).animate(_controller);

    // 监听动画状态
    _controller.addStatusListener((status) {
      print('Animation status: $status');
    });

    // 监听动画值变化
    _controller.addListener(() {
      print('Animation value: ${_animation.value}');
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Basic Animation')),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (context, child) {
            return Container(
              width: _sizeAnimation.value?.width,
              height: _sizeAnimation.value?.height,
              decoration: BoxDecoration(
                color: _colorAnimation.value,
                borderRadius: BorderRadius.circular(
                  _animation.value * 20,
                ),
              ),
              child: Center(
                child: Text(
                  '${(_animation.value * 100).toInt()}%',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 20 + _animation.value * 10,
                  ),
                ),
              ),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (_controller.isCompleted) {
            _controller.reverse();
          } else {
            _controller.forward();
          }
        },
        child: const Icon(Icons.play_arrow),
      ),
    );
  }
}

高级曲线动画

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// 自定义动画曲线
class CustomCurves {
  // 弹性曲线
  static const Curve elasticCurve = ElasticOutCurve(0.8);

  // 自定义贝塞尔曲线
  static const Curve customBezier = Cubic(0.25, 0.1, 0.25, 1.0);

  // 弹跳曲线
  static const Curve bounceCurve = BounceInOutCurve();

  // 呼吸效果曲线
  static Curve breathingCurve = _BreathingCurve();
}

class _BreathingCurve extends Curve {
  @override
  double transform(double t) {
    // 使用正弦函数创建呼吸效果
    return (1 - math.cos(t * 2 * math.pi)) / 2;
  }
}

// 复杂动画组合
class ComplexAnimationDemo extends StatefulWidget {
  @override
  _ComplexAnimationDemoState createState() => _ComplexAnimationDemoState();
}

class _ComplexAnimationDemoState extends State<ComplexAnimationDemo>
    with TickerProviderStateMixin {
  late AnimationController _mainController;
  late AnimationController _secondaryController;

  late Animation<double> _rotationAnimation;
  late Animation<double> _scaleAnimation;
  late Animation<double> _opacityAnimation;
  late Animation<Offset> _slideAnimation;
  late Animation<Color?> _gradientAnimation;

  @override
  void initState() {
    super.initState();

    // 主控制器
    _mainController = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );

    // 次级控制器
    _secondaryController = AnimationController(
      duration: const Duration(milliseconds: 1500),
      vsync: this,
    );

    // 旋转动画
    _rotationAnimation = Tween<double>(
      begin: 0,
      end: 2 * math.pi,
    ).animate(CurvedAnimation(
      parent: _mainController,
      curve: CustomCurves.elasticCurve,
    ));

    // 缩放动画
    _scaleAnimation = Tween<double>(
      begin: 0.8,
      end: 1.2,
    ).animate(CurvedAnimation(
      parent: _secondaryController,
      curve: CustomCurves.bounceCurve,
    ));

    // 透明度动画
    _opacityAnimation = Tween<double>(
      begin: 0.3,
      end: 1.0,
    ).animate(CurvedAnimation(
      parent: _mainController,
      curve: const Interval(0.2, 0.8, curve: Curves.easeInOut),
    ));

    // 滑动动画
    _slideAnimation = Tween<Offset>(
      begin: const Offset(-1, 0),
      end: const Offset(1, 0),
    ).animate(CurvedAnimation(
      parent: _secondaryController,
      curve: CustomCurves.customBezier,
    ));

    // 渐变色动画
    _gradientAnimation = RainbowColorTween(
      begin: Colors.purple,
      end: Colors.orange,
    ).animate(_mainController);

    // 启动次级动画延迟
    _secondaryController.repeat(reverse: true);
  }

  @override
  void dispose() {
    _mainController.dispose();
    _secondaryController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: [
              _gradientAnimation.value ?? Colors.blue,
              (_gradientAnimation.value ?? Colors.blue).withOpacity(0.8),
            ],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
        ),
        child: Center(
          child: AnimatedBuilder(
            animation: Listenable.merge([
              _mainController,
              _secondaryController,
            ]),
            builder: (context, child) {
              return Transform.rotate(
                angle: _rotationAnimation.value,
                child: Transform.scale(
                  scale: _scaleAnimation.value,
                  child: SlideTransition(
                    position: _slideAnimation,
                    child: Opacity(
                      opacity: _opacityAnimation.value,
                      child: Container(
                        width: 200,
                        height: 200,
                        decoration: BoxDecoration(
                          color: Colors.white.withOpacity(0.9),
                          borderRadius: BorderRadius.circular(20),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.black.withOpacity(0.3),
                              blurRadius: 20,
                              offset: const Offset(0, 10),
                            ),
                          ],
                        ),
                        child: const Center(
                          child: Text(
                            'Complex\nAnimation',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                              color: Colors.black87,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => _mainController.forward(),
            child: const Icon(Icons.play_arrow),
          ),
          const SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => _mainController.reverse(),
            child: const Icon(Icons.arrow_back),
          ),
          const SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () {
              if (_mainController.isAnimating) {
                _mainController.stop();
              } else {
                _mainController.repeat(reverse: true);
              }
            },
            child: const Icon(Icons.repeat),
          ),
        ],
      ),
    );
  }
}

物理模拟动画

弹簧动画

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// 自定义弹簧物理动画
class SpringAnimation extends StatefulWidget {
  const SpringAnimation({Key? key}) : super(key: key);

  @override
  _SpringAnimationState createState() => _SpringAnimationState();
}

class _SpringAnimationState extends State<SpringAnimation>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _springAnimation;

  double _targetPosition = 0;
  double _currentPosition = 0;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(milliseconds: 1000),
      vsync: this,
    );

    // 使用SpringSimulation
    _springAnimation = _controller.drive(
      SpringSimulation(
        const SpringDescription(
          mass: 1,
          stiffness: 100,
          damping: 10,
        ),
        0,
        1,
        0,
      ),
    );
  }

  void _animateTo(double target) {
    _targetPosition = target;

    // 更新弹簧模拟
    final simulation = SpringSimulation(
      const SpringDescription(
        mass: 1,
        stiffness: 200,
        damping: 15,
      ),
      _currentPosition,
      _targetPosition,
      0,
    );

    _controller.animateWith(simulation);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Spring Animation')),
      body: GestureDetector(
        onPanUpdate: (details) {
          setState(() {
            _currentPosition = details.localPosition.dy;
          });
        },
        onPanEnd: (details) {
          _animateTo(_currentPosition);
        },
        child: AnimatedBuilder(
          animation: _springAnimation,
          builder: (context, child) {
            return Stack(
              children: [
                // 目标位置指示器
                Positioned(
                  top: 200,
                  left: 0,
                  right: 0,
                  child: Container(
                    height: 2,
                    color: Colors.grey.withOpacity(0.5),
                  ),
                ),
                // 弹簧小球
                Positioned(
                  top: 200 + _springAnimation.value * 200,
                  left: 0,
                  right: 0,
                  child: Center(
                    child: Container(
                      width: 60,
                      height: 60,
                      decoration: BoxDecoration(
                        color: Colors.blue,
                        shape: BoxShape.circle,
                        boxShadow: [
                          BoxShadow(
                            color: Colors.blue.withOpacity(0.3),
                            blurRadius: 20,
                            spreadRadius: 5,
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
                // 弹簧连线
                CustomPaint(
                  size: Size.infinite,
                  painter: SpringPainter(
                    startY: 200,
                    endY: 200 + _springAnimation.value * 200,
                    coils: 10,
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

// 弹簧绘制器
class SpringPainter extends CustomPainter {
  final double startY;
  final double endY;
  final int coils;

  SpringPainter({
    required this.startY,
    required this.endY,
    this.coils = 10,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey
      ..strokeWidth = 2;

    final path = Path();
    final width = size.width / 2;

    path.moveTo(width, startY);

    final coilHeight = (endY - startY) / coils;

    for (int i = 0; i <= coils; i++) {
      final y = startY + (i * coilHeight);
      final x = width + (i % 2 == 0 ? -30 : 30);

      if (i == 0) {
        path.lineTo(x, y);
      } else {
        path.quadraticBezierTo(
          x - (i % 2 == 0 ? -30 : 30),
          y - coilHeight / 2,
          x,
          y,
        );
      }
    }

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

重力和碰撞动画

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// 物理引擎动画
class PhysicsEngineDemo extends StatefulWidget {
  const PhysicsEngineDemo({Key? key}) : super(key: key);

  @override
  _PhysicsEngineDemoState createState() => _PhysicsEngineDemoState();
}

class _PhysicsEngineDemoState extends State<PhysicsEngineDemo>
    with TickerProviderStateMixin {
  late AnimationController _physicsController;
  final List<Particle> _particles = [];
  late double _gravity = 980; // 像素/秒²
  late double _restitution = 0.8; // 弹性系数

  @override
  void initState() {
    super.initState();

    _physicsController = AnimationController(
      duration: const Duration(hours: 1), // 很长的持续时间
      vsync: this,
    );

    // 初始化粒子
    _initializeParticles();

    // 开始物理模拟
    _startPhysicsSimulation();
  }

  void _initializeParticles() {
    final random = math.Random();

    for (int i = 0; i < 20; i++) {
      _particles.add(Particle(
        position: Offset(
          random.nextDouble() * 300 + 50,
          random.nextDouble() * 100,
        ),
        velocity: Offset(
          (random.nextDouble() - 0.5) * 200,
          random.nextDouble() * -100,
        ),
        radius: random.nextDouble() * 20 + 10,
        color: Colors.primaries[random.nextInt(Colors.primaries.length)],
      ));
    }
  }

  void _startPhysicsSimulation() {
    _physicsController.addListener(_updatePhysics);
    _physicsController.repeat();
  }

  void _updatePhysics() {
    final deltaTime = 0.016; // 假设60FPS

    for (final particle in _particles) {
      // 应用重力
      particle.velocity += Offset(0, _gravity * deltaTime);

      // 更新位置
      particle.position += particle.velocity * deltaTime;

      // 边界碰撞检测
      _handleBoundaryCollision(particle);
    }

    // 粒子间碰撞检测
    _handleParticleCollisions();

    setState(() {});
  }

  void _handleBoundaryCollision(Particle particle) {
    final size = MediaQuery.of(context).size;
    final radius = particle.radius;

    // 底部碰撞
    if (particle.position.dy + radius > size.height - 100) {
      particle.position.dy = size.height - 100 - radius;
      particle.velocity = Offset(
        particle.velocity.dx,
        -particle.velocity.dy * _restitution,
      );
    }

    // 左右边界碰撞
    if (particle.position.dx - radius < 0) {
      particle.position.dx = radius;
      particle.velocity = Offset(
        -particle.velocity.dx * _restitution,
        particle.velocity.dy,
      );
    }

    if (particle.position.dx + radius > size.width) {
      particle.position.dx = size.width - radius;
      particle.velocity = Offset(
        -particle.velocity.dx * _restitution,
        particle.velocity.dy,
      );
    }
  }

  void _handleParticleCollisions() {
    for (int i = 0; i < _particles.length; i++) {
      for (int j = i + 1; j < _particles.length; j++) {
        final p1 = _particles[i];
        final p2 = _particles[j];

        final distance = (p1.position - p2.position).distance;
        final minDistance = p1.radius + p2.radius;

        if (distance < minDistance) {
          // 碰撞发生
          _resolveCollision(p1, p2, distance, minDistance);
        }
      }
    }
  }

  void _resolveCollision(Particle p1, Particle p2, double distance, double minDistance) {
    // 计算碰撞法线
    final normal = (p2.position - p1.position) / distance;

    // 分离重叠的粒子
    final overlap = minDistance - distance;
    final separation = normal * (overlap / 2);
    p1.position -= separation;
    p2.position += separation;

    // 计算相对速度
    final relativeVelocity = p1.velocity - p2.velocity;
    final velocityAlongNormal = relativeVelocity.dx * normal.dx + relativeVelocity.dy * normal.dy;

    // 如果粒子正在分离,不需要处理
    if (velocityAlongNormal > 0) return;

    // 计算碰撞后的速度
    final restitution = _restitution;
    final impulse = 2 * velocityAlongNormal / 2; // 假设质量相等
    final impulseVector = normal * impulse * restitution;

    p1.velocity -= impulseVector;
    p2.velocity += impulseVector;
  }

  void _addExplosion(Offset center) {
    for (final particle in _particles) {
      final direction = particle.position - center;
      final distance = direction.distance;

      if (distance > 0) {
        final force = 50000 / distance; // 爆炸力随距离衰减
        final normalizedDirection = direction / distance;

        particle.velocity += normalizedDirection * force;
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Physics Engine')),
      body: GestureDetector(
        onTapUp: (details) {
          _addExplosion(details.localPosition);
        },
        child: Container(
          width: double.infinity,
          height: double.infinity,
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [
                Colors.blue.shade100,
                Colors.purple.shade100,
              ],
            ),
          ),
          child: Stack(
            children: _particles.map((particle) {
              return Positioned(
                left: particle.position.dx - particle.radius,
                top: particle.position.dy - particle.radius,
                child: Container(
                  width: particle.radius * 2,
                  height: particle.radius * 2,
                  decoration: BoxDecoration(
                    color: particle.color,
                    shape: BoxShape.circle,
                    boxShadow: [
                      BoxShadow(
                        color: particle.color.withOpacity(0.5),
                        blurRadius: particle.radius / 2,
                        spreadRadius: particle.radius / 4,
                      ),
                    ],
                  ),
                ),
              );
            }).toList(),
          ),
        ),
      ),
      floatingActionButton: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          FloatingActionButton(
            onPressed: () {
              setState(() {
                _particles.clear();
                _initializeParticles();
              });
            },
            child: const Icon(Icons.refresh),
          ),
          const SizedBox(height: 10),
          FloatingActionButton(
            onPressed: () {
              _gravity = _gravity == 980 ? 1960 : 980;
            },
            child: const Icon(Icons.arrow_downward),
          ),
        ],
      ),
    );
  }
}

// 粒子类
class Particle {
  Offset position;
  Offset velocity;
  double radius;
  Color color;

  Particle({
    required this.position,
    required this.velocity,
    required this.radius,
    required this.color,
  });
}

高级动画效果

粒子系统

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// 高性能粒子系统
class ParticleSystem extends StatefulWidget {
  final ParticleConfig config;

  const ParticleSystem({
    Key? key,
    required this.config,
  }) : super(key: key);

  @override
  _ParticleSystemState createState() => _ParticleSystemState();
}

class _ParticleSystemState extends State<ParticleSystem>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  final List<Particle2> _particles = [];
  final Random _random = Random();

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(seconds: 10),
      vsync: this,
    );

    _initializeParticles();
    _controller.repeat();
  }

  void _initializeParticles() {
    for (int i = 0; i < widget.config.particleCount; i++) {
      _particles.add(Particle2(
        position: widget.config.emitterPosition,
        velocity: _generateInitialVelocity(),
        acceleration: widget.config.acceleration,
        life: _random.nextDouble() * widget.config.lifeSpan + widget.config.lifeSpan / 2,
        maxLife: widget.config.lifeSpan,
        size: _random.nextDouble() * widget.config.sizeVariation + widget.config.baseSize,
        color: widget.config.colors[_random.nextInt(widget.config.colors.length)],
        fadeOut: widget.config.fadeOut,
      ));
    }
  }

  Offset _generateInitialVelocity() {
    final angle = _random.nextDouble() * 2 * math.pi;
    final speed = _random.nextDouble() * widget.config.speedVariation + widget.config.baseSpeed;

    return Offset(
      math.cos(angle) * speed,
      math.sin(angle) * speed,
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: ParticlePainter(
        particles: _particles,
        controller: _controller,
      ),
      child: Container(),
    );
  }
}

// 粒子配置类
class ParticleConfig {
  final Offset emitterPosition;
  final int particleCount;
  final double baseSpeed;
  final double speedVariation;
  final double baseSize;
  final double sizeVariation;
  final double lifeSpan;
  final List<Color> colors;
  final bool fadeOut;
  final Offset acceleration;

  ParticleConfig({
    required this.emitterPosition,
    this.particleCount = 100,
    this.baseSpeed = 100,
    this.speedVariation = 50,
    this.baseSize = 5,
    this.sizeVariation = 3,
    this.lifeSpan = 3,
    this.colors = const [Colors.white, Colors.yellow, Colors.orange],
    this.fadeOut = true,
    this.acceleration = const Offset(0, 50),
  });
}

// 粒子类
class Particle2 {
  Offset position;
  Offset velocity;
  Offset acceleration;
  double life;
  final double maxLife;
  final double size;
  final Color color;
  final bool fadeOut;

  Particle2({
    required this.position,
    required this.velocity,
    this.acceleration = const Offset(0, 50),
    required this.life,
    required this.maxLife,
    required this.size,
    required this.color,
    this.fadeOut = true,
  });

  void update(double deltaTime) {
    // 更新速度
    velocity += acceleration * deltaTime;

    // 更新位置
    position += velocity * deltaTime;

    // 更新生命值
    life -= deltaTime;
  }

  Paint getPaint() {
    final paint = Paint()
      ..color = color;

    if (fadeOut) {
      paint.color = color.withOpacity(life / maxLife);
    }

    return paint;
  }
}

// 粒子绘制器
class ParticlePainter extends CustomPainter {
  final List<Particle2> particles;
  final AnimationController controller;

  ParticlePainter({
    required this.particles,
    required this.controller,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final deltaTime = 0.016; // 假设60FPS

    for (final particle in particles) {
      if (particle.life <= 0) {
        // 重置粒子
        _resetParticle(particle);
      }

      particle.update(deltaTime);

      if (particle.life > 0) {
        final paint = particle.getPaint();
        canvas.drawCircle(
          particle.position,
          particle.size * (particle.life / particle.maxLife),
          paint,
        );
      }
    }
  }

  void _resetParticle(Particle particle) {
    final random = Random();
    final angle = random.nextDouble() * 2 * math.pi;
    final speed = random.nextDouble() * 50 + 100;

    particle.position = const Offset(200, 300);
    particle.velocity = Offset(
      math.cos(angle) * speed,
      math.sin(angle) * speed,
    );
    particle.life = particle.maxLife;
  }

  @override
  bool shouldRepaint(covariant ParticlePainter oldDelegate) {
    return true;
  }
}

形态变换动画

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// 复杂的形状变换动画
class MorphingAnimation extends StatefulWidget {
  const MorphingAnimation({Key? key}) : super(key: key);

  @override
  _MorphingAnimationState createState() => _MorphingAnimationState();
}

class _MorphingAnimationState extends State<MorphingAnimation>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _morphProgress;

  final List<Offset> _circlePoints = [];
  final List<Offset> _squarePoints = [];
  final List<Offset> _starPoints = [];
  final List<Offset> _currentPoints = [];

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(seconds: 4),
      vsync: this,
    );

    _morphProgress = Tween<double>(
      begin: 0,
      end: 1,
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    ));

    _generateShapePoints();
    _controller.repeat(reverse: true);
  }

  void _generateShapePoints() {
    final center = const Offset(200, 200);
    final radius = 100;
    const pointCount = 32;

    // 生成圆形点
    for (int i = 0; i < pointCount; i++) {
      final angle = (i / pointCount) * 2 * math.pi;
      _circlePoints.add(
        Offset(
          center.dx + radius * math.cos(angle),
          center.dy + radius * math.sin(angle),
        ),
      );
    }

    // 生成方形点
    final squareSize = radius * 1.5;
    for (int i = 0; i < pointCount; i++) {
      final t = i / pointCount;
      late Offset point;

      if (t < 0.25) {
        // 上边
        final localT = t * 4;
        point = Offset(
          center.dx - squareSize / 2 + localT * squareSize,
          center.dy - squareSize / 2,
        );
      } else if (t < 0.5) {
        // 右边
        final localT = (t - 0.25) * 4;
        point = Offset(
          center.dx + squareSize / 2,
          center.dy - squareSize / 2 + localT * squareSize,
        );
      } else if (t < 0.75) {
        // 下边
        final localT = (t - 0.5) * 4;
        point = Offset(
          center.dx + squareSize / 2 - localT * squareSize,
          center.dy + squareSize / 2,
        );
      } else {
        // 左边
        final localT = (t - 0.75) * 4;
        point = Offset(
          center.dx - squareSize / 2,
          center.dy + squareSize / 2 - localT * squareSize,
        );
      }

      _squarePoints.add(point);
    }

    // 生成星形点
    const starPoints = 5;
    const outerRadius = radius;
    const innerRadius = radius * 0.5;

    for (int i = 0; i < pointCount; i++) {
      final pointIndex = (i * starPoints / pointCount).floor();
      final t = (i * starPoints / pointCount) - pointIndex;

      late Offset point;
      final angle = (pointIndex / starPoints) * 2 * math.pi - math.pi / 2;

      if (pointIndex % 2 == 0) {
        // 外顶点
        point = Offset(
          center.dx + outerRadius * math.cos(angle),
          center.dy + outerRadius * math.sin(angle),
        );
      } else {
        // 内顶点
        point = Offset(
          center.dx + innerRadius * math.cos(angle),
          center.dy + innerRadius * math.sin(angle),
        );
      }

      _starPoints.add(point);
    }

    // 初始化当前点为圆形
    _currentPoints.addAll(_circlePoints);
  }

  void _updateMorphing() {
    final progress = _morphProgress.value;

    // 根据进度选择不同的形状组合
    List<Offset> targetPoints;
    String shapeName;

    if (progress < 0.33) {
      // 圆形到方形
      final t = progress / 0.33;
      targetPoints = _squarePoints;
      shapeName = 'Circle → Square';
    } else if (progress < 0.66) {
      // 方形到星形
      final t = (progress - 0.33) / 0.33;
      targetPoints = _starPoints;
      shapeName = 'Square → Star';
    } else {
      // 星形到圆形
      final t = (progress - 0.66) / 0.34;
      targetPoints = _circlePoints;
      shapeName = 'Star → Circle';
    }

    // 使用插值更新当前点
    for (int i = 0; i < _currentPoints.length; i++) {
      final startPoint = _currentPoints[i];
      final endPoint = targetPoints[i];

      // 根据整体进度计算插值
      var t = _morphProgress.value;

      // 应用缓动效果
      t = _applyEasing(t);

      _currentPoints[i] = Offset.lerp(startPoint, endPoint, t)!;
    }
  }

  double _applyEasing(double t) {
    // 使用自定义缓动函数
    return t < 0.5
        ? 2 * t * t
        : 1 - math.pow(-2 * t + 2, 2) / 2;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Morphing Animation'),
        actions: [
          IconButton(
            icon: const Icon(Icons.play_arrow),
            onPressed: () {
              if (_controller.isAnimating) {
                _controller.stop();
              } else {
                _controller.forward();
              }
            },
          ),
        ],
      ),
      body: AnimatedBuilder(
        animation: _morphProgress,
        builder: (context, child) {
          _updateMorphing();

          return CustomPaint(
            painter: MorphingPainter(
              points: _currentPoints,
              color: Colors.blue,
              strokeWidth: 3,
            ),
            child: const SizedBox.expand(),
          );
        },
      ),
    );
  }
}

// 变形绘制器
class MorphingPainter extends CustomPainter {
  final List<Offset> points;
  final Color color;
  final double strokeWidth;

  MorphingPainter({
    required this.points,
    required this.color,
    this.strokeWidth = 2,
  });

  @override
  void paint(Canvas canvas, Size size) {
    if (points.isEmpty) return;

    final paint = Paint()
      ..color = color
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.round;

    final path = Path()
      ..addPolygon(points, true);

    // 绘制形状轮廓
    canvas.drawPath(path, paint);

    // 填充半透明颜色
    final fillPaint = Paint()
      ..color = color.withOpacity(0.2)
      ..style = PaintingStyle.fill;

    canvas.drawPath(path, fillPaint);

    // 绘制顶点
    for (final point in points) {
      canvas.drawCircle(point, 4, Paint()..color = color);
    }
  }

  @override
  bool shouldRepaint(covariant MorphingPainter oldDelegate) {
    return oldDelegate.points != points;
  }
}

手势驱动动画

拖拽和手势动画

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// 手势驱动的弹性动画
class GestureAnimation extends StatefulWidget {
  const GestureAnimation({Key? key}) : super(key: key);

  @override
  _GestureAnimationState createState() => _GestureAnimationState();
}

class _GestureAnimationState extends State<GestureAnimation>
    with TickerProviderStateMixin {
  late AnimationController _animationController;
  late AnimationController _bounceController;

  final GlobalKey _cardKey = GlobalKey();

  double _rotation = 0;
  double _scale = 1;
  Offset _position = const Offset(0, 0);
  bool _isDragging = false;

  @override
  void initState() {
    super.initState();

    _animationController = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );

    _bounceController = AnimationController(
      duration: const Duration(milliseconds: 600),
      vsync: this,
    );
  }

  void _onPanStart(DragStartDetails details) {
    setState(() {
      _isDragging = true;
    });

    _animationController.stop();
  }

  void _onPanUpdate(DragUpdateDetails details) {
    final RenderBox renderBox = context.findRenderObject() as RenderBox;
    final localPosition = renderBox.globalToLocal(details.globalPosition);

    setState(() {
      _position = localPosition - const Offset(200, 150);

      // 根据拖拽距离计算旋转
      _rotation = _position.dx * 0.01;

      // 拖拽时略微放大
      _scale = 1.05;
    });
  }

  void _onPanEnd(DragEndDetails details) {
    setState(() {
      _isDragging = false;
    });

    // 计算回弹动画
    final velocity = details.velocity.pixelsPerSecond;
    final distance = (_position.distance + 100).clamp(0.0, 300.0);

    // 使用弹性动画返回原位
    _animationController.animateTo(0).then((_) {
      // 触发弹跳效果
      _bounceController.forward().then((_) {
        _bounceController.reverse();
      });
    });

    // 添加惯性效果
    _animateWithInertia(velocity, distance);
  }

  void _animateWithInertia(Offset velocity, double distance) {
    final inertiaAnimation = Tween<Offset>(
      begin: _position,
      end: Offset.zero,
    ).animate(CurvedAnimation(
      parent: _animationController,
      curve: Curves.elasticOut,
    ));

    inertiaAnimation.addListener(() {
      setState(() {
        _position = inertiaAnimation.value;
        _rotation = _position.dx * 0.01;
        _scale = 1 + distance * 0.0002 * (1 - inertiaAnimation.value.distance);
      });
    });

    _animationController.forward(from: _animationController.value);
  }

  void _onTap() {
    // 点击时的弹跳动画
    _bounceController.forward().then((_) {
      _bounceController.reverse();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Gesture Animation')),
      body: Center(
        child: GestureDetector(
          onPanStart: _onPanStart,
          onPanUpdate: _onPanUpdate,
          onPanEnd: _onPanEnd,
          onTap: _onTap,
          child: Container(
            width: 400,
            height: 400,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.grey.shade300),
              borderRadius: BorderRadius.circular(20),
            ),
            child: Stack(
              children: [
                // 参考网格
                CustomPaint(
                  size: const Size(400, 400),
                  painter: GridPainter(),
                ),
                // 可拖拽卡片
                AnimatedBuilder(
                  animation: Listenable.merge([
                    _animationController,
                    _bounceController,
                  ]),
                  builder: (context, child) {
                    return Transform.translate(
                      offset: _position,
                      child: Transform.rotate(
                        angle: _rotation,
                        child: Transform.scale(
                          scale: _scale * (1 + _bounceController.value * 0.1),
                          child: Container(
                            key: _cardKey,
                            width: 300,
                            height: 200,
                            decoration: BoxDecoration(
                              gradient: LinearGradient(
                                colors: [
                                  Colors.blue.shade400,
                                  Colors.purple.shade600,
                                ],
                                begin: Alignment.topLeft,
                                end: Alignment.bottomRight,
                              ),
                              borderRadius: BorderRadius.circular(20),
                              boxShadow: [
                                BoxShadow(
                                  color: Colors.black.withOpacity(_isDragging ? 0.3 : 0.2),
                                  blurRadius: _isDragging ? 20 : 10,
                                  offset: Offset(
                                    _isDragging ? _position.dx * 0.1 : 0,
                                    _isDragging ? _position.dy * 0.1 : 5,
                                  ),
                                ),
                              ],
                            ),
                            child: const Center(
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  Icon(
                                    Icons.touch_app,
                                    color: Colors.white,
                                    size: 48,
                                  ),
                                  SizedBox(height: 8),
                                  Text(
                                    'Drag Me!',
                                    style: TextStyle(
                                      color: Colors.white,
                                      fontSize: 24,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          ),
                        ),
                      ),
                    );
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

// 网格绘制器
class GridPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey.shade300
      ..strokeWidth = 0.5;

    const gridSize = 50.0;

    // 绘制垂直线
    for (double x = 0; x < size.width; x += gridSize) {
      canvas.drawLine(
        Offset(x, 0),
        Offset(x, size.height),
        paint,
      );
    }

    // 绘制水平线
    for (double y = 0; y < size.height; y += gridSize) {
      canvas.drawLine(
        Offset(0, y),
        Offset(size.width, y),
        paint,
      );
    }

    // 绘制中心十字
    final centerPaint = Paint()
      ..color = Colors.grey.shade500
      ..strokeWidth = 1;

    canvas.drawLine(
      const Offset(200, 0),
      const Offset(200, 400),
      centerPaint,
    );

    canvas.drawLine(
      const Offset(0, 200),
      const Offset(400, 200),
      centerPaint,
    );
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

性能优化技巧

动画性能监控

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// 动画性能监控器
class AnimationPerformanceMonitor {
  static final AnimationPerformanceMonitor _instance = AnimationPerformanceMonitor._internal();
  factory AnimationPerformanceMonitor() => _instance;
  AnimationPerformanceMonitor._internal();

  final List<PerformanceMetric> _metrics = [];
  bool _isMonitoring = false;

  void startMonitoring() {
    if (_isMonitoring) return;

    _isMonitoring = true;
    WidgetsBinding.instance.addPostFrameCallback(_onFrame);
  }

  void stopMonitoring() {
    _isMonitoring = false;
  }

  void _onFrame(Duration timestamp) {
    if (!_isMonitoring) return;

    final renderer = WidgetsBinding.instance.renderObject;
    final frameTime = timestamp.inMicroseconds / 1000;

    // 记录性能指标
    final metric = PerformanceMetric(
      timestamp: timestamp,
      frameTime: frameTime,
      fps: 1000 / frameTime,
      cpuUsage: _getCpuUsage(),
      memoryUsage: _getMemoryUsage(),
    );

    _metrics.add(metric);

    // 保持最近1000帧的指标
    if (_metrics.length > 1000) {
      _metrics.removeAt(0);
    }

    // 如果性能低于阈值,发出警告
    if (metric.fps < 30) {
      print('⚠️ Low FPS detected: ${metric.fps.toStringAsFixed(1)}');
      _analyzePerformanceIssue(metric);
    }

    WidgetsBinding.instance.addPostFrameCallback(_onFrame);
  }

  void _analyzePerformanceIssue(PerformanceMetric metric) {
    // 分析性能问题
    if (metric.memoryUsage > 100 * 1024 * 1024) { // 100MB
      print('💾 High memory usage detected');
    }

    if (metric.cpuUsage > 80) {
      print('🔥 High CPU usage detected');
    }
  }

  double _getCpuUsage() {
    // 模拟CPU使用率
    return 30 + (math.Random().nextDouble() * 40);
  }

  double _getMemoryUsage() {
    // 模拟内存使用
    return 50 * 1024 * 1024 + (math.Random().nextDouble() * 50 * 1024 * 1024);
  }

  PerformanceReport generateReport() {
    if (_metrics.isEmpty) {
      return PerformanceReport.empty();
    }

    final avgFPS = _metrics.fold(0.0, (sum, m) => sum + m.fps) / _metrics.length;
    final minFPS = _metrics.map((m) => m.fps).reduce((a, b) => a < b ? a : b);
    final maxFPS = _metrics.map((m) => m.fps).reduce((a, b) => a > b ? a : b);
    final avgMemory = _metrics.fold(0.0, (sum, m) => sum + m.memoryUsage) / _metrics.length;

    return PerformanceReport(
      avgFPS: avgFPS,
      minFPS: minFPS,
      maxFPS: maxFPS,
      avgMemory: avgMemory,
      frameCount: _metrics.length,
      lowFPSFrames: _metrics.where((m) => m.fps < 30).length,
    );
  }

  void logReport() {
    final report = generateReport();
    print('📊 Animation Performance Report:');
    print('   Average FPS: ${report.avgFPS.toStringAsFixed(1)}');
    print('   Min FPS: ${report.minFPS.toStringAsFixed(1)}');
    print('   Max FPS: ${report.maxFPS.toStringAsFixed(1)}');
    print('   Low FPS Frames: ${report.lowFPSFrames}');
    print('   Average Memory: ${(report.avgMemory / 1024 / 1024).toStringAsFixed(1)} MB');
  }
}

// 性能指标类
class PerformanceMetric {
  final DateTime timestamp;
  final double frameTime;
  final double fps;
  final double cpuUsage;
  final double memoryUsage;

  PerformanceMetric({
    required this.timestamp,
    required this.frameTime,
    required this.fps,
    required this.cpuUsage,
    required this.memoryUsage,
  });
}

// 性能报告类
class PerformanceReport {
  final double avgFPS;
  final double minFPS;
  final double maxFPS;
  final double avgMemory;
  final int frameCount;
  final int lowFPSFrames;

  PerformanceReport({
    required this.avgFPS,
    required this.minFPS,
    required this.maxFPS,
    required this.avgMemory,
    required this.frameCount,
    required this.lowFPSFrames,
  });

  factory PerformanceReport.empty() {
    return PerformanceReport(
      avgFPS: 0,
      minFPS: 0,
      maxFPS: 0,
      avgMemory: 0,
      frameCount: 0,
      lowFPSFrames: 0,
    );
  }

  bool get isPerformant => avgFPS >= 55 && lowFPSFrames < frameCount * 0.05;
  String get performanceRating {
    if (avgFPS >= 58) return 'Excellent';
    if (avgFPS >= 55) return 'Good';
    if (avgFPS >= 45) return 'Fair';
    return 'Poor';
  }
}

优化的动画实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// 高性能动画组件
class OptimizedAnimatedWidget extends StatefulWidget {
  final Widget child;
  final Animation<double> animation;
  final bool enableCache;
  final Duration cacheDuration;

  const OptimizedAnimatedWidget({
    Key? key,
    required this.child,
    required this.animation,
    this.enableCache = true,
    this.cacheDuration = const Duration(milliseconds: 100),
  }) : super(key: key);

  @override
  _OptimizedAnimatedWidgetState createState() => _OptimizedAnimatedWidgetState();
}

class _OptimizedAnimatedWidgetState extends State<OptimizedAnimatedWidget>
    with AutomaticKeepAliveClientMixin {
  Widget? _cachedChild;
  DateTime? _lastCacheTime;
  double _lastAnimationValue = -1;

  @override
  bool get wantKeepAlive => true;

  @override
  void didUpdateWidget(OptimizedAnimatedWidget oldWidget) {
    super.didUpdateWidget(oldWidget);

    // 如果动画变化,清除缓存
    if (widget.animation != oldWidget.animation) {
      _cachedChild = null;
    }
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);

    final animationValue = widget.animation.value;

    // 检查是否可以使用缓存的子组件
    if (widget.enableCache &&
        _cachedChild != null &&
        _lastCacheTime != null &&
        DateTime.now().difference(_lastCacheTime!) < widget.cacheDuration &&
        (animationValue - _lastAnimationValue).abs() < 0.01) {
      return _cachedChild!;
    }

    // 使用RepaintBoundary限制重绘范围
    return RepaintBoundary(
      child: AnimatedBuilder(
        animation: widget.animation,
        builder: (context, child) {
          // 构建优化后的组件
          final optimizedChild = Transform.scale(
            scale: animationValue,
            alignment: Alignment.center,
            child: FadeTransition(
              opacity: widget.animation,
              child: widget.child,
            ),
          );

          // 缓存构建结果
          if (widget.enableCache) {
            _cachedChild = optimizedChild;
            _lastCacheTime = DateTime.now();
            _lastAnimationValue = animationValue;
          }

          return optimizedChild;
        },
      ),
    );
  }
}

// 批量动画管理器
class BatchAnimationManager {
  final Map<String, AnimationController> _controllers = {};
  final Map<String, TickerProvider> _tickers = {};
  Timer? _batchTimer;
  final List<VoidCallback> _pendingAnimations = [];

  // 注册动画控制器
  void registerController(
    String id,
    AnimationController controller,
    TickerProvider vsync,
  ) {
    _controllers[id] = controller;
    _tickers[id] = vsync;
  }

  // 批量启动动画
  void batchStartAnimations(List<String> controllerIds) {
    for (final id in controllerIds) {
      final controller = _controllers[id];
      if (controller != null && !controller.isAnimating) {
        _pendingAnimations.add(() => controller.forward());
      }
    }

    _scheduleBatch();
  }

  // 批量停止动画
  void batchStopAnimations(List<String> controllerIds) {
    for (final id in controllerIds) {
      final controller = _controllers[id];
      if (controller != null && controller.isAnimating) {
        _pendingAnimations.add(() => controller.stop());
      }
    }

    _scheduleBatch();
  }

  void _scheduleBatch() {
    _batchTimer?.cancel();
    _batchTimer = Timer(const Duration(milliseconds: 16), () {
      // 在下一帧执行所有动画
      for (final animation in _pendingAnimations) {
        animation();
      }
      _pendingAnimations.clear();
      _batchTimer = null;
    });
  }

  // 销毁指定控制器
  void disposeController(String id) {
    _controllers[id]?.dispose();
    _controllers.remove(id);
    _tickers.remove(id);
  }

  // 销毁所有控制器
  void disposeAll() {
    for (final controller in _controllers.values) {
      controller.dispose();
    }
    _controllers.clear();
    _tickers.clear();
    _batchTimer?.cancel();
    _pendingAnimations.clear();
  }
}

// 使用示例
class AdvancedAnimationExample extends StatefulWidget {
  const AdvancedAnimationExample({Key? key}) : super(key: key);

  @override
  _AdvancedAnimationExampleState createState() => _AdvancedAnimationExampleState();
}

class _AdvancedAnimationExampleState extends State<AdvancedAnimationExample>
    with TickerProviderStateMixin {
  late AnimationController _mainController;
  late AnimationController _secondaryController;
  final BatchAnimationManager _batchManager = BatchAnimationManager();

  @override
  void initState() {
    super.initState();

    _mainController = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );

    _secondaryController = AnimationController(
      duration: const Duration(milliseconds: 800),
      vsync: this,
    );

    // 注册到批量管理器
    _batchManager.registerController('main', _mainController, this);
    _batchManager.registerController('secondary', _secondaryController, this);

    // 启动性能监控
    AnimationPerformanceMonitor().startMonitoring();
  }

  @override
  void dispose() {
    _mainController.dispose();
    _secondaryController.dispose();
    _batchManager.disposeAll();
    super.dispose();
  }

  void _startBatchAnimation() {
    _batchManager.batchStartAnimations(['main', 'secondary']);
  }

  void _logPerformance() {
    AnimationPerformanceMonitor().logReport();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Advanced Animation'),
        actions: [
          IconButton(
            onPressed: _startBatchAnimation,
            icon: const Icon(Icons.play_arrow),
          ),
          IconButton(
            onPressed: _logPerformance,
            icon: const Icon(Icons.analytics),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            OptimizedAnimatedWidget(
              animation: _mainController,
              enableCache: true,
              child: Container(
                width: 200,
                height: 200,
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    colors: [Colors.blue, Colors.purple],
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                  ),
                  borderRadius: BorderRadius.circular(20),
                ),
                child: const Center(
                  child: Text(
                    'Optimized',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ),
            ),
            const SizedBox(height: 40),
            OptimizedAnimatedWidget(
              animation: _secondaryController,
              child: Container(
                width: 150,
                height: 150,
                decoration: BoxDecoration(
                  color: Colors.orange,
                  borderRadius: BorderRadius.circular(15),
                ),
                child: const Center(
                  child: Text(
                    'Cached',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

总结

Flutter动画系统的强大之处在于:

核心特性:

  1. 灵活的AnimationController和Tween系统
  2. 丰富的动画曲线选择
  3. 自定义绘制能力
  4. 硬件加速支持

高级技巧:

  1. 复杂动画编排和组合
  2. 物理模拟和真实感效果
  3. 手势驱动的交互动画
  4. 性能监控和优化策略

性能优化:

  1. 使用AnimatedBuilder避免不必要的重建
  2. 实现缓存机制减少计算开销
  3. 合理使用RepaintBoundary
  4. 批量管理动画控制器

最佳实践:

  1. 保持动画在60FPS
  2. 避免在动画中进行复杂计算
  3. 使用硬件加速的属性
  4. 合理设置动画持续时间和曲线

掌握这些高级动画技巧,将让你能够创造出令人惊叹的 Flutter 应用体验。


相关资源: