一、 关键类
1、AnimationController
AnimationController 类是Flutter中动画的基础类,它提供了一个动画控制器,用于控制动画的播放、暂停、停止等操作。
2、TickerProvider
TickerProvider 是一个接口,它定义了创建 Ticker 对象的方法,并指定的 onTick 回调关联,Ticker 对象用于控制动画的播放速度和时间。
3、SingleTickerProviderStateMixin
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider
SingleTickerProviderStateMixin 是一个 mixin 类,它实现了 TickerProvider 接口,并提供了创建 Ticker 对象的方法。
单一Ticker提供者:
当你的 State 类只需要一个 AnimationController(即一个 Ticker)时,应使用 SingleTickerProviderStateMixin。
此 Mixin 保证在该 State 实例的生命期内仅创建和管理一个 Ticker。
如果尝试在此 State 中创建多个 AnimationController,会抛出异常,提示"multiple tickers were created"。
适用于包含单个简单动画的场景,如页面过渡动画、单个控件的旋转、淡入淡出等。
当你确信一个 State 中不会有多个并发动画时,使用此 Mixin 可以避免不必要的资源消耗。
4、TickerProviderStateMixin
mixin TickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider
TickerProviderStateMixin 是一个 mixin 类,它实现了 TickerProvider 接口,并提供了创建 Ticker 对象的方法。
多Ticker提供者:
当你的 State 类需要管理多个独立的 AnimationController(每个对应一个 Ticker)时,应使用 TickerProviderStateMixin。
这个 Mixin 允许你在同一 State 实例中创建任意数量的 AnimationController,为每个动画控制器分配单独的 Ticker。
适用于复杂场景,比如一个页面中有多个独立动画需要同时运行或按需控制
适用于需要管理多个并发动画的复杂场景,如多元素协同动画、不同触发条件下运行的不同动画序列等。
当页面或者组件内包含多个相互独立或有依赖关系的动画控制器时,使用此 Mixin 可确保所有动画都能正确创建和管理各自的 Ticker。
5、SchedulerBinding
SchedulerBinding 是 Flutter 框架中一个核心的绑定类,它负责管理 Flutter 应用程序中的任务调度与时间线事件处理。以下是对 SchedulerBinding 类的主要功能和用途的详细说明:
a. 任务调度:SchedulerBinding
负责协调 Flutter 应用中的异步操作、动画帧绘制、事件处理等任务的执行顺序。它实现了基于优先级的任务队列,确保不同类型的任务在适当的时机得到调度。
通过 scheduleFrame() 方法,SchedulerBinding 可以触发下一帧的绘制请求。每当设备屏幕刷新时,此方法会被调用,以驱动 Flutter 的渲染循环。
b.生命周期管理:
SchedulerBinding 监听并响应应用程序的生命周期事件,如启动、暂停、恢复和停止。开发者可以通过监听其提供的 WidgetsBindingObserver 接口中的相关回调方法(如 didChangeAppLifecycleState())来适时调整应用状态或资源。
c.定时器管理:
SchedulerBinding 提供了创建、管理和取消定时器的功能。使用 Timer.run()、Timer.periodic() 等方法可以安排一次性或周期性任务。这些定时器任务会在特定时间点被调度执行,且遵循优先级规则。
手势与事件处理:
SchedulerBinding 整合了对触摸、键盘输入等用户交互事件的处理。它将这些事件转换为 GestureEvent 或 PointerEvent,并通过调度机制传递给相应的 GestureDetector 或 Listener 组件进行处理。
e. 调度监听与回调:
开发者可以注册监听 SchedulerBinding 上的特定事件或阶段,例如:
addPersistentFrameCallback():添加一个持续的帧回调,该回调将在每一帧绘制前被调用。
addPostFrameCallback():添加一个在当前帧绘制完成后执行的回调,常用于需要在视图布局完成后进行的操作。
addTimingsCallback():添加一个时间线回调,用于收集和分析应用性能数据。
二、 深入源码
初始化一个AnimationController的时候
注册了一个回调函数,并创建了一个Ticker对象
AnimationController({
double? value,
this.duration,
this.reverseDuration,
this.debugLabel,
this.lowerBound = 0.0,
this.upperBound = 1.0,
this.animationBehavior = AnimationBehavior.normal,
required TickerProvider vsync,
}) : assert(upperBound >= lowerBound),
_direction = _AnimationDirection.forward {
_ticker = vsync.createTicker(_tick);
_internalSetValue(value ?? lowerBound);
}
这个_tick函数的代码
void _tick(Duration elapsed) {
_lastElapsedDuration = elapsed;
final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;
assert(elapsedInSeconds >= 0.0);
_value = clampDouble(_simulation!.x(elapsedInSeconds), lowerBound, upperBound);
if (_simulation!.isDone(elapsedInSeconds)) {
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.completed :
AnimationStatus.dismissed;
stop(canceled: false);
}
notifyListeners();
_checkStatusChanged();
}
Ticker类
@override
Ticker createTicker(TickerCallback onTick) {
assert(() {
if (_ticker == null) {
return true;
}
_ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by ${describeIdentity(this)}' : null);
_updateTickerModeNotifier();
_updateTicker(); // Sets _ticker.mute correctly.
return _ticker!;
}
_tick函数被封装成了Ticker对象,往后看在哪调用的,后面有解释
void notifyListeners() {
final List<VoidCallback> localListeners = _listeners.toList(growable: false);
for (final VoidCallback listener in localListeners) {
try {
if (_listeners.contains(listener)) {
listener();
}
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'animation library',
context: ErrorDescription('while notifying listeners for $runtimeType'),
informationCollector: collector,
));
}
}
}
}
notifyListeners()这个就是通知监听,就是我们调用的 addListener的方法
我们看看_checkStatusChanged方法
void _checkStatusChanged() {
final AnimationStatus newStatus = status;
if (_lastReportedStatus != newStatus) {
_lastReportedStatus = newStatus;
notifyStatusListeners(newStatus);
}
}
这个方法就是通知状态的监听,就是我们调用的addStatusListener的方法。
而真正开启动画的,就是调用controller.forward()
TickerFuture forward({ double? from }) {
_direction = _AnimationDirection.forward;
if (from != null) {
value = from;
}
return _animateToInternal(upperBound);
}
_animateToInternal 方法又调用了_startSimulation方法
TickerFuture _startSimulation(Simulation simulation) {
assert(!isAnimating);
_simulation = simulation;
_lastElapsedDuration = Duration.zero;
_value = clampDouble(simulation.x(0.0), lowerBound, upperBound);
final TickerFuture result = _ticker!.start();
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.forward :
AnimationStatus.reverse;
_checkStatusChanged();
return result;
}
从上面的代码我们可以看到调用了,我们刚才在AnimationController初始化创建的Ticker对象的start方法
TickerFuture start() {
_future = TickerFuture._();
if (shouldScheduleTick) {
scheduleTick();
}
if (SchedulerBinding.instance.schedulerPhase.index > SchedulerPhase.idle.index &&
SchedulerBinding.instance.schedulerPhase.index < SchedulerPhase.postFrameCallbacks.index) {
_startTime = SchedulerBinding.instance.currentFrameTimeStamp;
}
return _future!;
}
我们看看scheduleTick();
void scheduleTick({ bool rescheduling = false }) {
_animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);
}
该函数用于安排一个帧回调函数_tick,以便在下一帧被执行。rescheduling参数决定是否允许重新安排回调。通过SchedulerBinding.instance.scheduleFrameCallback方法来实现。
这个_tick函数是Ticker类的方法,不是AnimationController的方法,下面的_onTick这个方法才是调用AnimationController的_tick函数,这个_tick函数就是我们上面传入Ticker对象里的onTick。就是在这里调用的
void _tick(Duration timeStamp) {
_animationId = null;
_startTime ??= timeStamp;
_onTick(timeStamp - _startTime!);
// The onTick callback may have scheduled another tick already, for
// example by calling stop then start again.
if (shouldScheduleTick) {
scheduleTick(rescheduling: true);
}
}
SchedulerBinding类的
int scheduleFrameCallback(FrameCallback callback, { bool rescheduling = false }) {
scheduleFrame();
_nextFrameCallbackId += 1;
_transientCallbacks[_nextFrameCallbackId] = _FrameCallbackEntry(callback, rescheduling: rescheduling);
return _nextFrameCallbackId;
}
scheduleFrame()用来调度一个绘制帧
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) {
return;
}
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
void ensureFrameCallbacksRegistered() {
platformDispatcher.onBeginFrame ??= _handleBeginFrame;
platformDispatcher.onDrawFrame ??= _handleDrawFrame;
}
void _handleBeginFrame(Duration rawTimeStamp) {
if (_warmUpFrame) {
_rescheduleAfterWarmUpFrame = true;
return;
}
handleBeginFrame(rawTimeStamp);
}
_transientCallbacks[_nextFrameCallbackId] = _FrameCallbackEntry(callback, rescheduling: rescheduling), 这行代码是将我们的_tick函数封装成一个_FrameCallbackEntry对象,然后存到了_transientCallbacks 这个Map对象里。
从这里我们就可以猜到,应该就是等到下一个垂直同步信号的到来,然后再遍历调用_transientCallbacks的对象里的回调方法
我们找到了遍历_transientCallbacks的方法是handleBeginFrame
void handleBeginFrame(Duration? rawTimeStamp) {
_frameTimelineTask?.start('Frame');
_firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null) {
_lastRawTimeStamp = rawTimeStamp;
}
_hasScheduledFrame = false;
try {
// TRANSIENT FRAME CALLBACKS
_frameTimelineTask?.start('Animate');
_schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{};
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
if (!_removedIds.contains(id)) {
_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
}
});
_removedIds.clear();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
从上面代码可以看出,_handleBeginFrame函数注册了一下,然后等垂直同步信号的到来.
platformDispatcher.scheduleFrame()这个就是开始请求绘制帧的。
当垂直同步信号到来后,就开始回调handleBeginFrame方法,接着就开始遍历调用_transientCallbacks里的对象,也就会回调上面说的_tick函数,这样整个动画就开始了。
三、最后
其实还有很多源码细节,我就不去抠了,先从整体上把握整个流程我感觉就可以了。