源码级别解析 · Flutter SDK animation 包
2026-03-05 | 每日技术深度解读
Flutter 动画系统采用分层架构设计,每一层都有明确的职责边界:
设计理念:动画系统采用组合优于继承的原则,通过 Animation + Tween + Curve 的组合实现灵活的动画效果。
Animation<T> 是整个动画系统的核心抽象,它代表一个可能随时间变化的值。
abstract class Animation<T> extends Listenable
implements ValueListenable<T> {
const Animation();
// 核心属性
T get value; // 当前值
AnimationStatus get status; // 当前状态
// 状态判断
bool get isDismissed => status.isDismissed;
bool get isCompleted => status.isCompleted;
bool get isAnimating => status.isAnimating;
// 监听器管理
void addListener(VoidCallback listener);
void addStatusListener(AnimationStatusListener listener);
// 链式调用
Animation<U> drive<U>(Animatable<U> child);
}
AnimationStatus 定义了动画的四种状态:
enum AnimationStatus {
dismissed, // 停止在起点 (value = 0.0)
forward, // 正向运行 (0.0 → 1.0)
reverse, // 反向运行 (1.0 → 0.0)
completed; // 停止在终点 (value = 1.0)
}
| 状态 | 含义 | value | isAnimating |
|---|---|---|---|
| dismissed | 停止在起点 | 0.0 | false |
| forward | 正向播放 | 0.0→1.0 | true |
| reverse | 反向播放 | 1.0→0.0 | true |
| completed | 停止在终点 | 1.0 | false |
┌─────────────────────────────────────────┐
│ │
▼ │
┌──────────┐ forward() ┌──────────┐ │
│ dismissed│──────────────────────▶│ forward │ │
└──────────┘ └──────────┘ │
▲ │ │
│ │ │
│ reverse() │ │
│ ▼ │
┌──────────┐ ┌──────────┐ │
│completed │◀────────────────────│completed │──┘
└──────────┘ (到达终点时) └──────────┘
状态转换触发条件:
forward() → dismissed/任意 → forwardreverse() → completed/任意 → reverseAnimation 提供两种监听器类型:
animation.addListener(() {
print('当前值: ${animation.value}');
setState(() {});
});
animation.addStatusListener((status) {
if (status == AnimationStatus.completed)
animation.reverse();
if (status == AnimationStatus.dismissed)
animation.forward();
});
Flutter 提供三个 Mixin 来简化监听器管理:
// 懒加载监听器 Mixin - 有监听器时才开始工作
mixin AnimationLazyListenerMixin {
int _listenerCounter = 0;
void didRegisterListener() {
if (_listenerCounter == 0)
didStartListening();
_listenerCounter += 1;
}
void didUnregisterListener() {
_listenerCounter -= 1;
if (_listenerCounter == 0)
didStopListening();
}
bool get isListening => _listenerCounter > 0;
}
实现 addListener/removeListener 协议:
mixin AnimationLocalListenersMixin {
final _listeners = HashedObserverList<VoidCallback>();
void addListener(VoidCallback listener) {
didRegisterListener();
_listeners.add(listener);
}
void removeListener(VoidCallback listener) {
if (_listeners.remove(listener))
didUnregisterListener();
}
void notifyListeners() {
for (final listener in _listeners.toList())
if (_listeners.contains(listener)) listener();
}
}
mixin AnimationLocalStatusListenersMixin {
final _statusListeners = ObserverList<AnimationStatusListener>();
void addStatusListener(AnimationStatusListener listener) {
didRegisterListener();
_statusListeners.add(listener);
}
void removeStatusListener(AnimationStatusListener listener) {
if (_statusListeners.remove(listener))
didUnregisterListener();
}
void notifyStatusListeners(AnimationStatus status) {
for (final listener in _statusListeners.toList())
if (_statusListeners.contains(listener))
listener(status);
}
}
AnimationController 是动画系统的核心控制器:
Animation<double>Ticker 驱动帧更新forward/reverse/stop/repeat/fling 等控制方法关键特性:AnimationController 是唯一一个"主动"产生值的 Animation,其他 Animation 都是被动响应值变化。
class AnimationController extends Animation<double>
with AnimationEagerListenerMixin,
AnimationLocalListenersMixin,
AnimationLocalStatusListenersMixin {
AnimationController({
double? value, // 初始值
this.duration, // 正向动画时长
this.reverseDuration, // 反向动画时长
this.lowerBound = 0.0, // 下界
this.upperBound = 1.0, // 上界
required TickerProvider vsync, // 必需
}) {
_ticker = vsync.createTicker(_tick);
_internalSetValue(value ?? lowerBound);
}
// 无界构造函数(用于物理模拟)
AnimationController.unbounded({
double value = 0.0,
required TickerProvider vsync,
}) : lowerBound = double.negativeInfinity,
upperBound = double.infinity;
}
Ticker 是帧回调的封装,每帧调用一次:
// 典型用法:State 实现 TickerProvider
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this, // Mixin 提供了 TickerProvider
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
每帧调用,驱动动画进度更新:
void _tick(Duration elapsed) {
_lastElapsedDuration = elapsed;
final double elapsedInSeconds =
elapsed.inMicroseconds.toDouble() /
Duration.microsecondsPerSecond;
// 通过 Simulation 计算当前值
_value = clampDouble(
_simulation!.x(elapsedInSeconds),
lowerBound, upperBound
);
// 检查动画是否完成
if (_simulation!.isDone(elapsedInSeconds)) {
_status = (_direction == _AnimationDirection.forward)
? AnimationStatus.completed
: AnimationStatus.dismissed;
stop(canceled: false);
}
notifyListeners(); // 通知值监听器
_checkStatusChanged(); // 通知状态监听器
}
enum _AnimationDirection { forward, reverse }
// forward 方法
TickerFuture forward({double? from}) {
_direction = _AnimationDirection.forward;
if (from != null) value = from;
return _animateToInternal(upperBound);
}
// reverse 方法
TickerFuture reverse({double? from}) {
_direction = _AnimationDirection.reverse;
if (from != null) value = from;
return _animateToInternal(lowerBound);
}
// toggle 方法
TickerFuture toggle({double? from}) {
_direction = isForwardOrCompleted
? _AnimationDirection.reverse
: _AnimationDirection.forward;
return _animateToInternal(
_direction == forward ? upperBound : lowerBound
);
}
驱动动画到指定目标值:
TickerFuture animateTo(
double target,
{Duration? duration, Curve curve = Curves.linear}
) {
_direction = _AnimationDirection.forward;
return _animateToInternal(target,
duration: duration, curve: curve);
}
// 内部实现
TickerFuture _animateToInternal(double target, ...) {
// 计算动画时长
var simulationDuration = duration;
if (simulationDuration == null) {
final double range = upperBound - lowerBound;
final double remainingFraction =
(target - _value).abs() / range;
simulationDuration = this.duration! * remainingFraction;
}
return _startSimulation(
_InterpolationSimulation(_value, target,
simulationDuration, curve, scale)
);
}
class _InterpolationSimulation extends Simulation {
_InterpolationSimulation(
this._begin, this._end,
Duration duration, this._curve, double scale
) : _durationInSeconds =
(duration.inMicroseconds * scale) /
Duration.microsecondsPerSecond;
@override
double x(double timeInSeconds) {
final double t = clampDouble(
timeInSeconds / _durationInSeconds, 0.0, 1.0);
return switch (t) {
0.0 => _begin,
1.0 => _end,
_ => _begin + (_end - _begin) * _curve.transform(t),
};
}
@override
bool isDone(double timeInSeconds) =>
timeInSeconds > _durationInSeconds;
}
TickerFuture repeat({
double? min,
double? max,
bool reverse = false, // 往返(乒乓效果)
Duration? period,
int? count, // null = 无限循环
}) {
min ??= lowerBound;
max ??= upperBound;
period ??= duration;
stop();
return _startSimulation(
_RepeatingSimulation(_value, min, max,
reverse, period!, _directionSetter, count),
);
}
// 使用示例
controller.repeat(reverse: true); // 往返无限循环
controller.repeat(count: 3); // 重复 3 次
class _RepeatingSimulation extends Simulation {
@override
double x(double timeInSeconds) {
final double totalTimeInSeconds = timeInSeconds + _initialT;
final double t = (totalTimeInSeconds / _periodInSeconds) % 1.0;
final bool isPlayingReverse =
(totalTimeInSeconds ~/ _periodInSeconds).isOdd;
if (reverse && isPlayingReverse) {
directionSetter(_AnimationDirection.reverse);
return ui.lerpDouble(max, min, t)!;
} else {
directionSetter(_AnimationDirection.forward);
return ui.lerpDouble(min, max, t)!;
}
}
@override
bool isDone(double timeInSeconds) {
return count != null &&
(timeInSeconds >= _exitTimeInSeconds);
}
}
基于弹簧物理的甩动动画:
TickerFuture fling({
double velocity = 1.0,
SpringDescription? springDescription,
}) {
springDescription ??= _kFlingSpringDescription;
_direction = velocity < 0.0
? _AnimationDirection.reverse
: _AnimationDirection.forward;
final double target = velocity < 0.0
? lowerBound - _kFlingTolerance.distance
: upperBound + _kFlingTolerance.distance;
final simulation = SpringSimulation(
springDescription, value, target, velocity * scale
)..tolerance = _kFlingTolerance;
stop();
return _startSimulation(simulation);
}
控制无障碍模式下动画的行为:
enum AnimationBehavior {
normal, // 禁用动画时缩短时长
preserve, // 不受影响
}
// 在 _animateToInternal 中的应用
final double scale = switch (animationBehavior) {
AnimationBehavior.normal
when SemanticsBinding.instance.disableAnimations => 0.05,
AnimationBehavior.normal || AnimationBehavior.preserve => 1.0,
};
// fling 中速度放大
AnimationBehavior.normal
when SemanticsBinding.instance.disableAnimations => 200.0
Tween(插值器)用于将 0.0-1.0 的动画值转换为任意类型:
AnimationController (0.0 → 1.0)
│
▼
┌───────────────────┐
│ Tween<Color> │ lerp(begin: red, end: blue)
└───────────────────┘
│
▼
Animation<Color> (red → blue)
核心概念:Animatable 是可动画值的抽象,Tween 是最常用的实现。
abstract class Animatable<T> {
const Animatable();
/// 将输入 t (0.0-1.0) 转换为输出值
T transform(double t);
/// 对 Animation 的当前值求值
T evaluate(Animation<double> animation) =>
transform(animation.value);
/// 创建一个由 parent 驱动的 Animation
Animation<T> animate(Animation<double> parent) {
return _AnimatedEvaluation<T>(parent, this);
}
/// 链式组合
Animatable<T> chain(Animatable<double> parent) {
return _ChainedEvaluation<T>(parent, this);
}
}
最常用的 Animatable 实现,执行线性插值:
class Tween<T> extends Animatable<T> {
Tween({this.begin, this.end});
T? begin; // 起始值
T? end; // 结束值
/// 线性插值
@protected
T lerp(double t) {
return (begin as dynamic) +
((end as dynamic) - (begin as dynamic)) * t as T;
}
@override
T transform(double t) {
if (t == 0.0) return begin as T;
if (t == 1.0) return end as T;
return lerp(t);
}
}
// 基本用法
final animation = Tween<double>(
begin: 0.0, end: 300.0,
).animate(_controller);
// 链式调用
final alignment = _controller.drive(
AlignmentTween(
begin: Alignment.topLeft,
end: Alignment.topRight,
),
);
// 结合 Curve
final alignment3 = _controller
.drive(CurveTween(curve: Curves.easeIn))
.drive(AlignmentTween(
begin: Alignment.topLeft,
end: Alignment.topRight,
));
| Tween 类型 | 用途 | lerp 实现 |
|---|---|---|
| ColorTween | 颜色插值 | Color.lerp() |
| SizeTween | 尺寸插值 | Size.lerp() |
| RectTween | 矩形插值 | Rect.lerp() |
| IntTween | 整数(四舍五入) | round() |
| StepTween | 整数(向下取整) | floor() |
| ConstantTween | 常量值 | 返回固定值 |
| CurveTween | 曲线变换 | curve.transform() |
class ColorTween extends Tween<Color?> {
ColorTween({super.begin, super.end});
@override
Color? lerp(double t) => Color.lerp(begin, end, t);
}
// 使用示例
final colorAnimation = ColorTween(
begin: Colors.red,
end: Colors.blue,
).animate(_controller);
AnimatedBuilder(
animation: colorAnimation,
builder: (context, child) {
return Container(color: colorAnimation.value);
},
)
注意:null 值代表"无颜色",与 Colors.transparent 不同。
class IntTween extends Tween<int> {
@override
int lerp(double t) =>
(begin! + (end! - begin!) * t).round();
}
// t=0.5 时: 0→100 = 50
class StepTween extends Tween<int> {
@override
int lerp(double t) =>
(begin! + (end! - begin!) * t).floor();
}
// t=0.99 时: 0→100 = 99
class CurveTween extends Animatable<double> {
CurveTween({required this.curve});
Curve curve;
@override
double transform(double t) {
if (t == 0.0 || t == 1.0) return t;
return curve.transform(t);
}
}
// 使用示例
final curvedAnimation = _controller.drive(
CurveTween(curve: Curves.easeInOut),
);
// 或通过 chain
final curvedAnimation2 = Tween<double>(
begin: 0.0, end: 100.0,
).chain(CurveTween(curve: Curves.easeInOut))
.animate(_controller);
class ReverseTween<T> extends Tween<T> {
ReverseTween(this.parent)
: super(begin: parent.end, end: parent.begin);
final Tween<T> parent;
@override
T lerp(double t) => parent.lerp(1.0 - t);
}
// 使用示例
final forwardTween = Tween<double>(begin: 0.0, end: 100.0);
final reverseTween = ReverseTween<double>(forwardTween);
// reverseTween.transform(0.0) = 100.0
// reverseTween.transform(1.0) = 0.0
Curve 定义动画的时序变换,让动画更自然:
线性动画 (Linear) 缓动动画 (Curved)
│ / │ ╭─────
│ / │ ╱
│/ │ ╱
└──────────> └──────────>
匀速变化 先快后慢 / 先慢后快
核心约定:Curve 必须满足 t=0.0 → 0.0,t=1.0 → 1.0。
abstract class ParametricCurve<T> {
const ParametricCurve();
/// 返回参数 t 对应的曲线值
T transform(double t) {
assert(t >= 0.0 && t <= 1.0);
return transformInternal(t);
}
/// 子类实现此方法
@protected
T transformInternal(double t) {
throw UnimplementedError();
}
}
@immutable
abstract class Curve extends ParametricCurve<double> {
const Curve();
@override
double transform(double t) {
// 确保边界值正确
if (t == 0.0 || t == 1.0) return t;
return super.transform(t);
}
/// 返回反转曲线
Curve get flipped => FlippedCurve(this);
}
// 线性曲线实现
class _Linear extends Curve {
const _Linear._();
@override
double transformInternal(double t) => t;
}
class Cubic extends Curve {
const Cubic(this.a, this.b, this.c, this.d);
final double a; // 第一控制点 x
final double b; // 第一控制点 y
final double c; // 第二控制点 x
final double d; // 第二控制点 y
@override
double transformInternal(double t) {
// 二分查找求解
var start = 0.0, end = 1.0;
while (true) {
final midpoint = (start + end) / 2;
final estimate = _evaluateCubic(a, c, midpoint);
if ((t - estimate).abs() < 0.001)
return _evaluateCubic(b, d, midpoint);
if (estimate < t) start = midpoint;
else end = midpoint;
}
}
}
double _evaluateCubic(double a, double b, double m) {
// 三阶贝塞尔公式
return 3 * a * (1 - m) * (1 - m) * m +
3 * b * (1 - m) * m * m +
m * m * m;
}
Cubic(0.4, 0.0, 0.2, 1.0) - Curves.easeInOutCubic
y │ ╭────────────
│ ╭─╯
1 │ ╭──╯
│ ╭──╯
0 │────────────╯
└──────────────────────────── x
0 1
| 曲线 | 效果 | 控制点 |
|---|---|---|
| Curves.linear | 线性 | - |
| Curves.ease | 标准缓动 | Cubic(0.25, 0.1, 0.25, 1.0) |
| Curves.easeIn | 慢入 | Cubic(0.42, 0, 1, 1) |
| Curves.easeOut | 慢出 | Cubic(0, 0, 0.58, 1) |
| Curves.easeInOut | 慢入慢出 | Cubic(0.42, 0, 0.58, 1) |
| Curves.elasticIn | 弹性进入 | 振荡曲线 |
| Curves.bounceOut | 弹跳 | 分段衰减 |
在指定时间区间内应用曲线:
class Interval extends Curve {
const Interval(this.begin, this.end, {this.curve = Curves.linear});
final double begin; // 开始时间点 (0.0-1.0)
final double end; // 结束时间点 (0.0-1.0)
final Curve curve;
@override
double transformInternal(double t) {
// 将 t 映射到 [begin, end] 区间
t = clampDouble((t - begin) / (end - begin), 0.0, 1.0);
if (t == 0.0 || t == 1.0) return t;
return curve.transform(t);
}
}
// 三个动画在不同时间段执行
final first = Tween<double>(begin: 0.0, end: 1.0)
.chain(CurveTween(curve: Interval(0.0, 0.33)))
.animate(_controller);
final second = Tween<double>(begin: 0.0, end: 1.0)
.chain(CurveTween(curve: Interval(0.33, 0.66)))
.animate(_controller);
final third = Tween<double>(begin: 0.0, end: 1.0)
.chain(CurveTween(curve: Interval(0.66, 1.0)))
.animate(_controller);
// 时间线:
// 0.0 ───── 0.33 ───── 0.66 ───── 1.0
// │ first │ second │ third │
阶跃曲线,在阈值点瞬间跳变:
class Threshold extends Curve {
const Threshold(this.threshold);
final double threshold;
@override
double transformInternal(double t) =>
t < threshold ? 0.0 : 1.0;
}
// 使用示例:在 50% 时瞬间切换
final jumpAnimation = _controller.drive(
CurveTween(curve: Threshold(0.5)),
);
// 0.0 ─────── 0.5 ─────── 1.0
// │ 0.0 │ 1.0 │
// ↑ 瞬间跳变
锯齿曲线,重复多次线性增长:
class SawTooth extends Curve {
const SawTooth(this.count);
final int count;
@override
double transformInternal(double t) {
t *= count;
return t - t.truncateToDouble(); // 取小数部分
}
}
// 重复 3 次的锯齿动画
final sawAnimation = _controller.drive(
CurveTween(curve: SawTooth(3)),
);
// 0.0 ─── 0.33 ─── 0.66 ─── 1.0
// │ ↗ │ ↗ │ ↗ │
class FlippedCurve extends Curve {
const FlippedCurve(this.curve);
final Curve curve;
@override
double transformInternal(double t) =>
1.0 - curve.transform(1.0 - t);
}
// 使用示例
final easeOut = Curves.easeIn.flipped;
// 原曲线 翻转后
// │ ╭─── │ ───╮
// │ ╱ │ ╲
// │──╯ │ ╰──
在指定点分割,前后使用不同曲线:
class Split extends Curve {
const Split(this.split, {
this.beginCurve = Curves.linear,
this.endCurve = Curves.easeOutCubic,
});
final double split;
final Curve beginCurve;
final Curve endCurve;
@override
double transform(double t) {
if (t < split) {
return lerpDouble(0, split,
beginCurve.transform(t / split))!;
} else {
return lerpDouble(split, 1,
endCurve.transform((t - split) / (1 - split)))!;
}
}
}
abstract class Curve2D extends ParametricCurve<Offset> {
const Curve2D();
/// 生成采样点
Iterable<Curve2DSample> generateSamples({
double start = 0.0,
double end = 1.0,
double tolerance = 1e-10,
});
/// 返回 x 对应的参数 t
double findInverse(double x);
}
class Curve2DSample {
const Curve2DSample(this.t, this.value);
final double t;
final Offset value;
}
class CatmullRomSpline extends Curve2D {
CatmullRomSpline(
List<Offset> controlPoints, {
double tension = 0.0,
Offset? startHandle,
Offset? endHandle,
});
@override
Offset transformInternal(double t) {
// 计算当前 t 对应的曲线段
// 使用三次多项式计算点
}
}
// 使用示例:自定义运动路径
final spline = CatmullRomSpline([
Offset(0, 0),
Offset(100, 200),
Offset(200, 100),
Offset(300, 300),
]);
class CatmullRomCurve extends Curve {
CatmullRomCurve(this.controlPoints, {this.tension = 0.0});
final List<Offset> controlPoints;
final double tension;
static bool validateControlPoints(
List<Offset>? controlPoints, {
double tension = 0.0,
List<String>? reasons,
}) {
// 验证:至少 2 个点,X 在 (0,1),单调递增
}
}
class TweenSequence<T> extends Animatable<T> {
TweenSequence(List<TweenSequenceItem<T>> items) {
_items.addAll(items);
// 计算总权重
var totalWeight = 0.0;
for (final item in _items)
totalWeight += item.weight;
// 计算每个 item 的时间区间
var start = 0.0;
for (var i = 0; i < _items.length; i++) {
final double end = i == _items.length - 1
? 1.0
: start + _items[i].weight / totalWeight;
_intervals.add(_Interval(start, end));
start = end;
}
}
}
class TweenSequenceItem<T> {
const TweenSequenceItem({
required this.tween,
required this.weight,
});
final Animatable<T> tween;
final double weight;
}
// 使用示例
final sequence = TweenSequence<double>([
TweenSequenceItem(
tween: Tween(begin: 0.0, end: 10.0),
weight: 0.4, // 占 40% 时间
),
TweenSequenceItem(
tween: ConstantTween(10.0), // 保持 10.0
weight: 0.2,
),
TweenSequenceItem(
tween: Tween(begin: 10.0, end: 5.0),
weight: 0.4,
),
]).animate(_controller);
class FlippedTweenSequence extends TweenSequence<double> {
FlippedTweenSequence(super.items);
@override
double transform(double t) => 1 - super.transform(1 - t);
}
// 原序列:0 → 10 → 10 → 5
// 翻转后:5 → 10 → 10 → 0
// 使用场景:反向播放时使用不同的序列
Flutter 提供多种组合动画的方式: