AnimationController 类是Flutter中动画的基础类,它提供了一个动画控制器,用于控制动画的播放、暂停、停止等操作。
TickerProvider 是一个接口,它定义了创建 Ticker 对象的方法,并指定的 onTick 回调关联,Ticker 对象用于控制动画的播放速度和时间。
mixin SingleTickerProviderStateMixin on State implements TickerProvider
SingleTickerProviderStateMixin 是一个 mixin 类,它实现了 TickerProvider 接口,并提供了创建 Ticker 对象的方法。
单一Ticker提供者:
当你的 State 类只需要一个 AnimationController(即一个 Ticker)时,应使用 SingleTickerProviderStateMixin。
此 Mixin 保证在该 State 实例的生命期内仅创建和管理一个 Ticker。
如果尝试在此 State 中创建多个 AnimationController,会抛出异常,提示“multiple tickers were created”。
适用于包含单个简单动画的场景,如页面过渡动画、单个控件的旋转、淡入淡出等。
当你确信一个 State 中不会有多个并发动画时,使用此 Mixin 可以避免不必要的资源消耗。
mixin TickerProviderStateMixin on State implements TickerProvider
TickerProviderStateMixin 是一个 mixin 类,它实现了 TickerProvider 接口,并提供了创建 Ticker 对象的方法。
多Ticker提供者:
当你的 State 类需要管理多个独立的 AnimationController(每个对应一个 Ticker)时,应使用 TickerProviderStateMixin。
这个 Mixin 允许你在同一 State 实例中创建任意数量的 AnimationController,为每个动画控制器分配单独的 Ticker。
适用于复杂场景,比如一个页面中有多个独立动画需要同时运行或按需控制
适用于需要管理多个并发动画的复杂场景,如多元素协同动画、不同触发条件下运行的不同动画序列等。
当页面或者组件内包含多个相互独立或有依赖关系的动画控制器时,使用此 Mixin 可确保所有动画都能正确创建和管理各自的 Ticker。
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 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 callbacks = _transientCallbacks;
_transientCallbacks = {};
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函数,这样整个动画就开始了。
其实还有很多源码细节,我就不去抠了,先从整体上把握整个流程我感觉就可以了。