Flutter动画框架之AnimationController源码解析(二)

AnimationController 是 Flutter 动画系统的核心控制器,主要负责:

  • 控制动画的播放、暂停、停止
  • 管理动画值的变化范围(0.0-1.0 或自定义边界)
  • 提供动画状态监听和值变化通知
  • 与 Ticker 系统集成,实现帧同步动画

主要功能

1. 生命周期

  • AnimationController() - 标准构造函数,有边界限制
  • AnimationController.unbounded() - 无边界构造函数,适用物理模拟
  • dispose() - 资源释放,防止内存泄漏
  • resync() - 重新同步 TickerProvider

2. 基础控制方法

  • forward() - 向前播放动画
  • reverse() - 反向播放动画
  • toggle() - 智能切换动画方向
  • stop() - 停止动画
  • reset() - 重置到初始状态

3. 高级动画控制

  • animateTo(target) - 动画到指定值
  • animateBack(target) - 反向动画到指定值
  • repeat() - 循环播放动画
  • fling() - 弹性动画效果
  • animateWith(simulation) - 自定义物理模拟

4. 状态和属性

  • value - 当前动画值
  • status - 动画状态(forward/reverse/completed/dismissed)
  • isAnimating - 是否正在动画
  • velocity - 当前速度
  • duration/reverseDuration - 动画持续时间

重要实战

1. 内存管理

scala 复制代码
// ✅ 正确的生命周期管理
class _MyWidgetState extends State<MyWidget> 
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this,
    );
  }
  
  @override
  void dispose() {
    _controller.dispose(); // 🔑 关键:防止内存泄漏
    super.dispose();
  }
}

2. 异常处理

csharp 复制代码
// ✅ 使用 TickerFuture.orCancel 处理取消
try {
  await _controller.forward().orCancel;
  // 动画完成后的逻辑
} on TickerCanceled {
  // 动画被取消时的处理
}
scss 复制代码
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/// @docImport 'package:flutter/widgets.dart';
/// @docImport 'package:flutter_test/flutter_test.dart';
library;

import 'dart:ui' as ui show lerpDouble;

import 'package:flutter/foundation.dart';
import 'package:flutter/physics.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/semantics.dart';

import 'animation.dart';
import 'curves.dart';
import 'listener_helpers.dart';

export 'package:flutter/physics.dart' show Simulation, SpringDescription;
export 'package:flutter/scheduler.dart' show TickerFuture, TickerProvider;

export 'animation.dart' show Animation, AnimationStatus;
export 'curves.dart' show Curve;

const String _flutterAnimationLibrary = 'package:flutter/animation.dart';

// Examples can assume:
// late AnimationController _controller, fadeAnimationController, sizeAnimationController;
// late bool dismissed;
// void setState(VoidCallback fn) { }

/// The direction in which an animation is running.
/// 动画运行的方向。
enum _AnimationDirection {
  /// The animation is running from beginning to end.
  /// 动画从开始到结束运行。
  forward,

  /// The animation is running backwards, from end to beginning.
  /// 动画从结束到开始反向运行。
  reverse,
}

final SpringDescription _kFlingSpringDescription = SpringDescription.withDampingRatio(
  mass: 1.0,
  stiffness: 500.0,
);

const Tolerance _kFlingTolerance = Tolerance(
  velocity: double.infinity,
  distance: 0.01,
);

/// Configures how an [AnimationController] behaves when animations are
/// disabled.
///
/// When [AccessibilityFeatures.disableAnimations] is true, the device is asking
/// Flutter to reduce or disable animations as much as possible. To honor this,
/// we reduce the duration and the corresponding number of frames for
/// animations. This enum is used to allow certain [AnimationController]s to opt
/// out of this behavior.
///
/// For example, the [AnimationController] which controls the physics simulation
/// for a scrollable list will have [AnimationBehavior.preserve], so that when
/// a user attempts to scroll it does not jump to the end/beginning too quickly.
/// 配置当动画被禁用时 [AnimationController] 的行为方式。
///
/// 当 [AccessibilityFeatures.disableAnimations] 为 true 时,设备要求
/// Flutter 尽可能减少或禁用动画。为了遵守这一点,
/// 我们减少动画的持续时间和相应的帧数。
/// 此枚举用于允许某些 [AnimationController] 选择退出此行为。
///
/// 例如,控制可滚动列表物理模拟的 [AnimationController]
/// 将具有 [AnimationBehavior.preserve],这样当用户尝试滚动时,
/// 它不会过快地跳到结束/开始位置。
enum AnimationBehavior {
  /// The [AnimationController] will reduce its duration when
  /// [AccessibilityFeatures.disableAnimations] is true.
  /// 当 [AccessibilityFeatures.disableAnimations] 为 true 时,
  /// [AnimationController] 将减少其持续时间。
  normal,

  /// The [AnimationController] will preserve its behavior.
  ///
  /// This is the default for repeating animations in order to prevent them from
  /// flashing rapidly on the screen if the widget does not take the
  /// [AccessibilityFeatures.disableAnimations] flag into account.
  /// [AnimationController] 将保持其行为。
  ///
  /// 这是重复动画的默认值,以防止如果 widget 没有考虑
  /// [AccessibilityFeatures.disableAnimations] 标志时,
  /// 它们在屏幕上快速闪烁。
  preserve,
}

/// A controller for an animation.
///
/// This class lets you perform tasks such as:
///
/// * Play an animation [forward] or in [reverse], or [stop] an animation.
/// * Set the animation to a specific [value].
/// * Define the [upperBound] and [lowerBound] values of an animation.
/// * Create a [fling] animation effect using a physics simulation.
///
/// By default, an [AnimationController] linearly produces values that range
/// from 0.0 to 1.0, during a given duration.
/// 动画的控制器。
///
/// 此类允许您执行以下任务:
///
/// * 播放动画 [forward] 或 [reverse],或 [stop] 动画。
/// * 将动画设置为特定的 [value]。
/// * 定义动画的 [upperBound] 和 [lowerBound] 值。
/// * 使用物理模拟创建 [fling] 动画效果。
///
/// 默认情况下,[AnimationController] 在给定的持续时间内
/// 线性产生从 0.0 到 1.0 的值。
///
/// When the animation is actively animating, the animation controller generates
/// a new value each time the device running your app is ready to display a new
/// frame (typically, this rate is around 60--120 values per second).
/// If the animation controller is associated with a [State]
/// through a [TickerProvider], then its updates will be silenced when that
/// [State]'s subtree is disabled as defined by [TickerMode]; time will still
/// elapse, and methods like [forward] and [stop] can still be called and
/// will change the value, but the controller will not generate new values
/// on its own.
/// 当动画正在活跃地动画时,动画控制器在运行您应用的设备
/// 准备显示新帧时生成新值(通常,此速率约为每秒 60-120 个值)。
/// 如果动画控制器通过 [TickerProvider] 与 [State] 关联,
/// 那么当该 [State] 的子树按 [TickerMode] 定义被禁用时,其更新将被静音;
/// 时间仍会流逝,像 [forward] 和 [stop] 这样的方法仍可以被调用
/// 并将改变值,但控制器不会自己生成新值。
///
/// ## Ticker providers
///
/// An [AnimationController] needs a [TickerProvider], which is configured using
/// the `vsync` argument on the constructor.
/// The constructor uses the [TickerProvider] to create a [Ticker], which
/// the [AnimationController] uses to step through the animation it controls.
///
/// For advice on obtaining a ticker provider, see [TickerProvider].
/// Typically the relevant [State] serves as the ticker provider,
/// after applying a suitable mixin (like [SingleTickerProviderStateMixin])
/// to cause the [State] subclass to implement [TickerProvider].
/// ## Ticker 提供者
///
/// [AnimationController] 需要一个 [TickerProvider],
/// 它使用构造函数上的 `vsync` 参数进行配置。
/// 构造函数使用 [TickerProvider] 创建一个 [Ticker],
/// [AnimationController] 使用它来逐步执行它控制的动画。
///
/// 有关获取 ticker 提供者的建议,请参阅 [TickerProvider]。
/// 通常相关的 [State] 作为 ticker 提供者,
/// 在应用合适的 mixin(如 [SingleTickerProviderStateMixin])后
/// 使 [State] 子类实现 [TickerProvider]。
///
/// ## Life cycle
///
/// An [AnimationController] should be [dispose]d when it is no longer needed.
/// This reduces the likelihood of leaks. When used with a [StatefulWidget], it
/// is common for an [AnimationController] to be created in the
/// [State.initState] method and then disposed in the [State.dispose] method.
/// ## 生命周期
///
/// 当不再需要 [AnimationController] 时应该 [dispose] 它。
/// 这减少了泄漏的可能性。当与 [StatefulWidget] 一起使用时,
/// 通常在 [State.initState] 方法中创建 [AnimationController],
/// 然后在 [State.dispose] 方法中释放它。
///
/// ## Using [Future]s with [AnimationController]
///
/// The methods that start animations return a [TickerFuture] object which
/// completes when the animation completes successfully, and never throws an
/// error; if the animation is canceled, the future never completes. This object
/// also has a [TickerFuture.orCancel] property which returns a future that
/// completes when the animation completes successfully, and completes with an
/// error when the animation is aborted.
///
/// This can be used to write code such as the `fadeOutAndUpdateState` method
/// below.
/// ## 将 [Future] 与 [AnimationController] 一起使用
///
/// 启动动画的方法返回一个 [TickerFuture] 对象,
/// 当动画成功完成时完成,并且永远不会抛出错误;
/// 如果动画被取消,future 永远不会完成。此对象
/// 还有一个 [TickerFuture.orCancel] 属性,它返回一个 future,
/// 当动画成功完成时完成,当动画被中止时以错误完成。
///
/// 这可以用来编写如下面的 `fadeOutAndUpdateState` 方法这样的代码。
///
/// {@tool snippet}
///
/// Here is a stateful `Foo` widget. Its [State] uses the
/// [SingleTickerProviderStateMixin] to implement the necessary
/// [TickerProvider], creating its controller in the [State.initState] method
/// and disposing of it in the [State.dispose] method. The duration of the
/// controller is configured from a property in the `Foo` widget; as that
/// changes, the [State.didUpdateWidget] method is used to update the
/// controller.
/// 这是一个有状态的 `Foo` widget。它的 [State] 使用
/// [SingleTickerProviderStateMixin] 来实现必要的 [TickerProvider],
/// 在 [State.initState] 方法中创建其控制器,
/// 并在 [State.dispose] 方法中释放它。控制器的持续时间
/// 从 `Foo` widget 中的属性配置;当该属性发生变化时,
/// 使用 [State.didUpdateWidget] 方法来更新控制器。
///
/// ```dart
/// class Foo extends StatefulWidget {
///   const Foo({ super.key, required this.duration });
///
///   final Duration duration;
///
///   @override
///   State<Foo> createState() => _FooState();
/// }
///
/// class _FooState extends State<Foo> with SingleTickerProviderStateMixin {
///   late AnimationController _controller;
///
///   @override
///   void initState() {
///     super.initState();
///     _controller = AnimationController(
///       vsync: this, // the SingleTickerProviderStateMixin
///       duration: widget.duration,
///     );
///   }
///
///   @override
///   void didUpdateWidget(Foo oldWidget) {
///     super.didUpdateWidget(oldWidget);
///     _controller.duration = widget.duration;
///   }
///
///   @override
///   void dispose() {
///     _controller.dispose();
///     super.dispose();
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return Container(); // ...
///   }
/// }
/// ```
/// {@end-tool}
/// {@tool snippet}
///
/// The following method (for a [State] subclass) drives two animation
/// controllers using Dart's asynchronous syntax for awaiting [Future] objects:
/// 以下方法(用于 [State] 子类)使用 Dart 的异步语法
/// 等待 [Future] 对象来驱动两个动画控制器:
///
/// ```dart
/// Future<void> fadeOutAndUpdateState() async {
///   try {
///     await fadeAnimationController.forward().orCancel;
///     await sizeAnimationController.forward().orCancel;
///     setState(() {
///       dismissed = true;
///     });
///   } on TickerCanceled {
///     // the animation got canceled, probably because we were disposed
///   }
/// }
/// ```
/// {@end-tool}
///
/// The assumption in the code above is that the animation controllers are being
/// disposed in the [State] subclass' override of the [State.dispose] method.
/// Since disposing the controller cancels the animation (raising a
/// [TickerCanceled] exception), the code here can skip verifying whether
/// [State.mounted] is still true at each step. (Again, this assumes that the
/// controllers are created in [State.initState] and disposed in
/// [State.dispose], as described in the previous section.)
/// 上面代码中的假设是动画控制器在 [State] 子类的
/// [State.dispose] 方法重写中被释放。
/// 由于释放控制器会取消动画(抛出 [TickerCanceled] 异常),
/// 这里的代码可以跳过在每一步验证 [State.mounted] 是否仍为 true。
///(再次强调,这假设控制器在 [State.initState] 中创建
/// 并在 [State.dispose] 中释放,如前一节所述。)
///
/// {@tool dartpad}
/// This example shows how to use [AnimationController] and
/// [SlideTransition] to create an animated digit like you might find
/// on an old pinball machine our your car's odometer.  New digit
/// values slide into place from below, as the old value slides
/// upwards and out of view. Taps that occur while the controller is
/// already animating cause the controller's
/// [AnimationController.duration] to be reduced so that the visuals
/// don't fall behind.
/// 此示例展示如何使用 [AnimationController] 和 [SlideTransition]
/// 创建动画数字,就像您可能在老式弹球机或汽车里程表上找到的那样。
/// 新的数字值从下方滑入位置,而旧值向上滑动并消失在视野之外。
/// 在控制器已经在动画时发生的点击会导致控制器的
/// [AnimationController.duration] 被减少,以便视觉效果不会落后。
///
/// ** See code in examples/api/lib/animation/animation_controller/animated_digit.0.dart **
/// {@end-tool}
///
/// See also:
///
///  * [Tween], the base class for converting an [AnimationController] to a
///    range of values of other types.
/// 另请参阅:
///
///  * [Tween],将 [AnimationController] 转换为其他类型值范围的基类。
class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
  /// Creates an animation controller.
  ///
  /// * `value` is the initial value of the animation. If defaults to the lower
  ///   bound.
  ///
  /// * [duration] is the length of time this animation should last.
  ///
  /// * [debugLabel] is a string to help identify this animation during
  ///   debugging (used by [toString]).
  ///
  /// * [lowerBound] is the smallest value this animation can obtain and the
  ///   value at which this animation is deemed to be dismissed.
  ///
  /// * [upperBound] is the largest value this animation can obtain and the
  ///   value at which this animation is deemed to be completed.
  ///
  /// * `vsync` is the required [TickerProvider] for the current context. It can
  ///   be changed by calling [resync]. See [TickerProvider] for advice on
  ///   obtaining a ticker provider.
  /// 创建动画控制器。
  ///
  /// * `value` 是动画的初始值。默认为下界。
  ///
  /// * [duration] 是此动画应持续的时间长度。
  ///
  /// * [debugLabel] 是在调试期间帮助识别此动画的字符串(由 [toString] 使用)。
  ///
  /// * [lowerBound] 是此动画可以获得的最小值,
  ///   也是此动画被视为已解除的值。
  ///
  /// * [upperBound] 是此动画可以获得的最大值,
  ///   也是此动画被视为已完成的值。
  ///
  /// * `vsync` 是当前上下文所需的 [TickerProvider]。
  ///   可以通过调用 [resync] 来更改它。有关获取 ticker 提供者的建议,
  ///   请参阅 [TickerProvider]。
  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 {
    if (kFlutterMemoryAllocationsEnabled) {
      _maybeDispatchObjectCreation();
    }
    _ticker = vsync.createTicker(_tick);
    _internalSetValue(value ?? lowerBound);
  }

  /// Creates an animation controller with no upper or lower bound for its
  /// value.
  ///
  /// * [value] is the initial value of the animation.
  ///
  /// * [duration] is the length of time this animation should last.
  ///
  /// * [debugLabel] is a string to help identify this animation during
  ///   debugging (used by [toString]).
  ///
  /// * `vsync` is the required [TickerProvider] for the current context. It can
  ///   be changed by calling [resync]. See [TickerProvider] for advice on
  ///   obtaining a ticker provider.
  ///
  /// This constructor is most useful for animations that will be driven using a
  /// physics simulation, especially when the physics simulation has no
  /// pre-determined bounds.
  /// 创建一个对其值没有上界或下界的动画控制器。
  ///
  /// * [value] 是动画的初始值。
  ///
  /// * [duration] 是此动画应持续的时间长度。
  ///
  /// * [debugLabel] 是在调试期间帮助识别此动画的字符串(由 [toString] 使用)。
  ///
  /// * `vsync` 是当前上下文所需的 [TickerProvider]。
  ///   可以通过调用 [resync] 来更改它。有关获取 ticker 提供者的建议,
  ///   请参阅 [TickerProvider]。
  ///
  /// 此构造函数对于将使用物理模拟驱动的动画最有用,
  /// 特别是当物理模拟没有预定边界时。
  AnimationController.unbounded({
    double value = 0.0,
    this.duration,
    this.reverseDuration,
    this.debugLabel,
    required TickerProvider vsync,
    this.animationBehavior = AnimationBehavior.preserve,
  }) : lowerBound = double.negativeInfinity,
       upperBound = double.infinity,
       _direction = _AnimationDirection.forward {
    if (kFlutterMemoryAllocationsEnabled) {
      _maybeDispatchObjectCreation();
    }
    _ticker = vsync.createTicker(_tick);
    _internalSetValue(value);
  }

  /// Dispatches event of object creation to [FlutterMemoryAllocations.instance].
  /// 将对象创建事件分发到 [FlutterMemoryAllocations.instance]。
  void _maybeDispatchObjectCreation() {
    if (kFlutterMemoryAllocationsEnabled) {
      FlutterMemoryAllocations.instance.dispatchObjectCreated(
        library: _flutterAnimationLibrary,
        className: '$AnimationController',
        object: this,
      );
    }
  }

  /// The value at which this animation is deemed to be dismissed.
  /// 此动画被视为已解除的值。
  final double lowerBound;

  /// The value at which this animation is deemed to be completed.
  /// 此动画被视为已完成的值。
  final double upperBound;

  /// A label that is used in the [toString] output. Intended to aid with
  /// identifying animation controller instances in debug output.
  /// 在 [toString] 输出中使用的标签。旨在帮助在调试输出中识别动画控制器实例。
  final String? debugLabel;

  /// The behavior of the controller when [AccessibilityFeatures.disableAnimations]
  /// is true.
  ///
  /// Defaults to [AnimationBehavior.normal] for the [AnimationController.new]
  /// constructor, and [AnimationBehavior.preserve] for the
  /// [AnimationController.unbounded] constructor.
  /// 当 [AccessibilityFeatures.disableAnimations] 为 true 时控制器的行为。
  ///
  /// 对于 [AnimationController.new] 构造函数默认为 [AnimationBehavior.normal],
  /// 对于 [AnimationController.unbounded] 构造函数默认为 [AnimationBehavior.preserve]。
  final AnimationBehavior animationBehavior;

  /// Returns an [Animation<double>] for this animation controller, so that a
  /// pointer to this object can be passed around without allowing users of that
  /// pointer to mutate the [AnimationController] state.
  /// 为此动画控制器返回一个 [Animation<double>],以便可以传递此对象的指针
  /// 而不允许该指针的用户改变 [AnimationController] 状态。
  Animation<double> get view => this;

  /// The length of time this animation should last.
  ///
  /// If [reverseDuration] is specified, then [duration] is only used when going
  /// [forward]. Otherwise, it specifies the duration going in both directions.
  /// 此动画应持续的时间长度。
  ///
  /// 如果指定了 [reverseDuration],则 [duration] 仅在 [forward] 时使用。
  /// 否则,它指定两个方向的持续时间。
  Duration? duration;

  /// The length of time this animation should last when going in [reverse].
  ///
  /// The value of [duration] is used if [reverseDuration] is not specified or
  /// set to null.
  /// 此动画在 [reverse] 时应持续的时间长度。
  ///
  /// 如果未指定 [reverseDuration] 或设置为 null,则使用 [duration] 的值。
  Duration? reverseDuration;

  Ticker? _ticker;

  /// Recreates the [Ticker] with the new [TickerProvider].
  /// 使用新的 [TickerProvider] 重新创建 [Ticker]。
  void resync(TickerProvider vsync) {
    final Ticker oldTicker = _ticker!;
    _ticker = vsync.createTicker(_tick);
    _ticker!.absorbTicker(oldTicker);
  }

  Simulation? _simulation;

  /// The current value of the animation.
  ///
  /// Setting this value notifies all the listeners that the value
  /// changed.
  ///
  /// Setting this value also stops the controller if it is currently
  /// running; if this happens, it also notifies all the status
  /// listeners.
  /// 动画的当前值。
  ///
  /// 设置此值会通知所有监听器值已更改。
  ///
  /// 设置此值还会停止控制器(如果它当前正在运行);
  /// 如果发生这种情况,它还会通知所有状态监听器。
  @override
  double get value => _value;
  late double _value;
  /// Stops the animation controller and sets the current value of the
  /// animation.
  ///
  /// The new value is clamped to the range set by [lowerBound] and
  /// [upperBound].
  ///
  /// Value listeners are notified even if this does not change the value.
  /// Status listeners are notified if the animation was previously playing.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// See also:
  ///
  ///  * [reset], which is equivalent to setting [value] to [lowerBound].
  ///  * [stop], which aborts the animation without changing its value or status
  ///    and without dispatching any notifications other than completing or
  ///    canceling the [TickerFuture].
  ///  * [forward], [reverse], [animateTo], [animateWith], [fling], and [repeat],
  ///    which start the animation controller.
  /// 停止动画控制器并设置动画的当前值。
  ///
  /// 新值被限制在由 [lowerBound] 和 [upperBound] 设置的范围内。
  ///
  /// 即使这不会改变值,也会通知值监听器。
  /// 如果动画之前正在播放,则会通知状态监听器。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 另请参阅:
  ///
  ///  * [reset],等同于将 [value] 设置为 [lowerBound]。
  ///  * [stop],中止动画而不改变其值或状态,
  ///    除了完成或取消 [TickerFuture] 外不分发任何通知。
  ///  * [forward]、[reverse]、[animateTo]、[animateWith]、[fling] 和 [repeat],
  ///    启动动画控制器。
  set value(double newValue) {
    stop();
    _internalSetValue(newValue);
    notifyListeners();
    _checkStatusChanged();
  }

  /// Sets the controller's value to [lowerBound], stopping the animation (if
  /// in progress), and resetting to its beginning point, or dismissed state.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// See also:
  ///
  ///  * [value], which can be explicitly set to a specific value as desired.
  ///  * [forward], which starts the animation in the forward direction.
  ///  * [stop], which aborts the animation without changing its value or status
  ///    and without dispatching any notifications other than completing or
  ///    canceling the [TickerFuture].
  /// 将控制器的值设置为 [lowerBound],停止动画(如果正在进行),
  /// 并重置到其开始点或已解除状态。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 另请参阅:
  ///
  ///  * [value],可以根据需要显式设置为特定值。
  ///  * [forward],在前进方向启动动画。
  ///  * [stop],中止动画而不改变其值或状态,
  ///    除了完成或取消 [TickerFuture] 外不分发任何通知。
  void reset() {
    value = lowerBound;
  }

  /// The rate of change of [value] per second.
  ///
  /// If [isAnimating] is false, then [value] is not changing and the rate of
  /// change is zero.
  /// 每秒 [value] 的变化率。
  ///
  /// 如果 [isAnimating] 为 false,则 [value] 没有变化,变化率为零。
  double get velocity {
    if (!isAnimating) {
      return 0.0;
    }
    return _simulation!.dx(lastElapsedDuration!.inMicroseconds.toDouble() / Duration.microsecondsPerSecond);
  }

  void _internalSetValue(double newValue) {
    _value = clampDouble(newValue, lowerBound, upperBound);
    if (_value == lowerBound) {
      _status = AnimationStatus.dismissed;
    } else if (_value == upperBound) {
      _status = AnimationStatus.completed;
    } else {
      _status = switch (_direction) {
        _AnimationDirection.forward => AnimationStatus.forward,
        _AnimationDirection.reverse => AnimationStatus.reverse,
      };
    }
  }

  /// The amount of time that has passed between the time the animation started
  /// and the most recent tick of the animation.
  ///
  /// If the controller is not animating, the last elapsed duration is null.
  /// 动画开始时间与动画最近一次滴答之间经过的时间量。
  ///
  /// 如果控制器没有在动画,则最后经过的持续时间为 null。
  Duration? get lastElapsedDuration => _lastElapsedDuration;
  Duration? _lastElapsedDuration;

  /// Whether this animation is currently animating in either the forward or reverse direction.
  ///
  /// This is separate from whether it is actively ticking. An animation
  /// controller's ticker might get muted, in which case the animation
  /// controller's callbacks will no longer fire even though time is continuing
  /// to pass. See [Ticker.muted] and [TickerMode].
  ///
  /// If the animation was stopped (e.g. with [stop] or by setting a new [value]),
  /// [isAnimating] will return `false` but the [status] will not change,
  /// so the value of [AnimationStatus.isAnimating] might still be `true`.
  /// 此动画当前是否正在前进或反向方向动画。
  ///
  /// 这与是否正在活跃地滴答是分开的。动画控制器的 ticker 可能会被静音,
  /// 在这种情况下,即使时间继续流逝,动画控制器的回调也不会再触发。
  /// 请参阅 [Ticker.muted] 和 [TickerMode]。
  ///
  /// 如果动画被停止(例如使用 [stop] 或设置新的 [value]),
  /// [isAnimating] 将返回 `false`,但 [status] 不会改变,
  /// 因此 [AnimationStatus.isAnimating] 的值可能仍为 `true`。
  @override
  bool get isAnimating => _ticker != null && _ticker!.isActive;

  _AnimationDirection _direction;

  @override
  AnimationStatus get status => _status;
  late AnimationStatus _status;

  /// Starts running this animation forwards (towards the end).
  ///
  /// Returns a [TickerFuture] that completes when the animation is complete.
  ///
  /// If [from] is non-null, it will be set as the current [value] before running
  /// the animation.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// During the animation, [status] is reported as [AnimationStatus.forward],
  /// which switches to [AnimationStatus.completed] when [upperBound] is
  /// reached at the end of the animation.
  /// 开始向前运行此动画(朝向结束)。
  ///
  /// 返回一个在动画完成时完成的 [TickerFuture]。
  ///
  /// 如果 [from] 不为 null,它将在运行动画之前设置为当前 [value]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 在动画期间,[status] 报告为 [AnimationStatus.forward],
  /// 当在动画结束时达到 [upperBound] 时切换到 [AnimationStatus.completed]。
  TickerFuture forward({ double? from }) {
    assert(() {
      if (duration == null) {
        throw FlutterError(
          'AnimationController.forward() called with no default duration.\n'
          'The "duration" property should be set, either in the constructor or later, before '
          'calling the forward() function.',
        );
      }
      return true;
    }());
    assert(
      _ticker != null,
      'AnimationController.forward() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    _direction = _AnimationDirection.forward;
    if (from != null) {
      value = from;
    }
    return _animateToInternal(upperBound);
  }

  /// Starts running this animation in reverse (towards the beginning).
  ///
  /// Returns a [TickerFuture] that completes when the animation is dismissed.
  ///
  /// If [from] is non-null, it will be set as the current [value] before running
  /// the animation.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// During the animation, [status] is reported as [AnimationStatus.reverse],
  /// which switches to [AnimationStatus.dismissed] when [lowerBound] is
  /// reached at the end of the animation.
  /// 开始反向运行此动画(朝向开始)。
  ///
  /// 返回一个在动画被解除时完成的 [TickerFuture]。
  ///
  /// 如果 [from] 不为 null,它将在运行动画之前设置为当前 [value]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 在动画期间,[status] 报告为 [AnimationStatus.reverse],
  /// 当在动画结束时达到 [lowerBound] 时切换到 [AnimationStatus.dismissed]。
  TickerFuture reverse({ double? from }) {
    assert(() {
      if (duration == null && reverseDuration == null) {
        throw FlutterError(
          'AnimationController.reverse() called with no default duration or reverseDuration.\n'
          'The "duration" or "reverseDuration" property should be set, either in the constructor or later, before '
          'calling the reverse() function.',
        );
      }
      return true;
    }());
    assert(
      _ticker != null,
      'AnimationController.reverse() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    _direction = _AnimationDirection.reverse;
    if (from != null) {
      value = from;
    }
    return _animateToInternal(lowerBound);
  }

  /// Toggles the direction of this animation, based on whether it [isForwardOrCompleted].
  ///
  /// Specifically, this function acts the same way as [reverse] if the [status] is
  /// either [AnimationStatus.forward] or [AnimationStatus.completed], and acts as
  /// [forward] for [AnimationStatus.reverse] or [AnimationStatus.dismissed].
  ///
  /// If [from] is non-null, it will be set as the current [value] before running
  /// the animation.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  /// 根据 [isForwardOrCompleted] 切换此动画的方向。
  ///
  /// 具体来说,如果 [status] 是 [AnimationStatus.forward] 或 [AnimationStatus.completed],
  /// 此函数的行为与 [reverse] 相同,对于 [AnimationStatus.reverse] 或 [AnimationStatus.dismissed]
  /// 则表现为 [forward]。
  ///
  /// 如果 [from] 不为 null,它将在运行动画之前设置为当前 [value]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  TickerFuture toggle({ double? from }) {
    assert(() {
      Duration? duration = this.duration;
      if (isForwardOrCompleted) {
        duration ??= reverseDuration;
      }
      if (duration == null) {
        throw FlutterError(
          'AnimationController.toggle() called with no default duration.\n'
          'The "duration" property should be set, either in the constructor or later, before '
          'calling the toggle() function.',
        );
      }
      return true;
    }());
    assert(
      _ticker != null,
      'AnimationController.toggle() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    _direction = isForwardOrCompleted ? _AnimationDirection.reverse : _AnimationDirection.forward;
    if (from != null) {
      value = from;
    }
    return _animateToInternal(switch (_direction) {
      _AnimationDirection.forward => upperBound,
      _AnimationDirection.reverse => lowerBound,
    });
  }

  /// Drives the animation from its current value to the given target, "forward".
  ///
  /// Returns a [TickerFuture] that completes when the animation is complete.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// During the animation, [status] is reported as [AnimationStatus.forward]
  /// regardless of whether `target` > [value] or not. At the end of the
  /// animation, when `target` is reached, [status] is reported as
  /// [AnimationStatus.completed].
  ///
  /// If the `target` argument is the same as the current [value] of the
  /// animation, then this won't animate, and the returned [TickerFuture] will
  /// be already complete.
  /// 将动画从当前值驱动到给定目标,"向前"。
  ///
  /// 返回一个在动画完成时完成的 [TickerFuture]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 在动画期间,无论 `target` > [value] 与否,[status] 都报告为 [AnimationStatus.forward]。
  /// 在动画结束时,当达到 `target` 时,[status] 报告为 [AnimationStatus.completed]。
  ///
  /// 如果 `target` 参数与动画的当前 [value] 相同,
  /// 则不会进行动画,返回的 [TickerFuture] 将已经完成。
  TickerFuture animateTo(double target, { Duration? duration, Curve curve = Curves.linear }) {
    assert(() {
      if (this.duration == null && duration == null) {
        throw FlutterError(
          'AnimationController.animateTo() called with no explicit duration and no default duration.\n'
          'Either the "duration" argument to the animateTo() method should be provided, or the '
          '"duration" property should be set, either in the constructor or later, before '
          'calling the animateTo() function.',
        );
      }
      return true;
    }());
    assert(
      _ticker != null,
      'AnimationController.animateTo() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    _direction = _AnimationDirection.forward;
    return _animateToInternal(target, duration: duration, curve: curve);
  }

  /// Drives the animation from its current value to the given target, "backward".
  ///
  /// Returns a [TickerFuture] that completes when the animation is complete.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// During the animation, [status] is reported as [AnimationStatus.reverse]
  /// regardless of whether `target` < [value] or not. At the end of the
  /// animation, when `target` is reached, [status] is reported as
  /// [AnimationStatus.dismissed].
  ///
  /// If the `target` argument is the same as the current [value] of the
  /// animation, then this won't animate, and the returned [TickerFuture] will
  /// be already complete.
  /// 将动画从当前值驱动到给定目标,"向后"。
  ///
  /// 返回一个在动画完成时完成的 [TickerFuture]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 在动画期间,无论 `target` < [value] 与否,[status] 都报告为 [AnimationStatus.reverse]。
  /// 在动画结束时,当达到 `target` 时,[status] 报告为 [AnimationStatus.dismissed]。
  ///
  /// 如果 `target` 参数与动画的当前 [value] 相同,
  /// 则不会进行动画,返回的 [TickerFuture] 将已经完成。
  TickerFuture animateBack(double target, { Duration? duration, Curve curve = Curves.linear }) {
    assert(() {
      if (this.duration == null && reverseDuration == null && duration == null) {
        throw FlutterError(
          'AnimationController.animateBack() called with no explicit duration and no default duration or reverseDuration.\n'
          'Either the "duration" argument to the animateBack() method should be provided, or the '
          '"duration" or "reverseDuration" property should be set, either in the constructor or later, before '
          'calling the animateBack() function.',
        );
      }
      return true;
    }());
    assert(
      _ticker != null,
      'AnimationController.animateBack() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    _direction = _AnimationDirection.reverse;
    return _animateToInternal(target, duration: duration, curve: curve);
  }

  TickerFuture _animateToInternal(double target, { Duration? duration, Curve curve = Curves.linear }) {
    final double scale = switch (animationBehavior) {
      // Since the framework cannot handle zero duration animations, we run it at 5% of the normal
      // duration to limit most animations to a single frame.
      // Ideally, the framework would be able to handle zero duration animations, however, the common
      // pattern of an eternally repeating animation might cause an endless loop if it weren't delayed
      // for at least one frame.
      AnimationBehavior.normal when SemanticsBinding.instance.disableAnimations => 0.05,
      AnimationBehavior.normal || AnimationBehavior.preserve => 1.0,
    };
    Duration? simulationDuration = duration;
    if (simulationDuration == null) {
      assert(!(this.duration == null && _direction == _AnimationDirection.forward));
      assert(!(this.duration == null && _direction == _AnimationDirection.reverse && reverseDuration == null));
      final double range = upperBound - lowerBound;
      final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
      final Duration directionDuration =
        (_direction == _AnimationDirection.reverse && reverseDuration != null)
        ? reverseDuration!
        : this.duration!;
      simulationDuration = directionDuration * remainingFraction;
    } else if (target == value) {
      // Already at target, don't animate.
      simulationDuration = Duration.zero;
    }
    stop();
    if (simulationDuration == Duration.zero) {
      if (value != target) {
        _value = clampDouble(target, lowerBound, upperBound);
        notifyListeners();
      }
      _status = (_direction == _AnimationDirection.forward) ?
        AnimationStatus.completed :
        AnimationStatus.dismissed;
      _checkStatusChanged();
      return TickerFuture.complete();
    }
    assert(simulationDuration > Duration.zero);
    assert(!isAnimating);
    return _startSimulation(_InterpolationSimulation(_value, target, simulationDuration, curve, scale));
  }

  /// Starts running this animation in the forward direction, and
  /// restarts the animation when it completes.
  ///
  /// Defaults to repeating between the [lowerBound] and [upperBound] of the
  /// [AnimationController] when no explicit value is set for [min] and [max].
  ///
  /// With [reverse] set to true, instead of always starting over at [min]
  /// the starting value will alternate between [min] and [max] values on each
  /// repeat. The [status] will be reported as [AnimationStatus.reverse] when
  /// the animation runs from [max] to [min].
  ///
  /// Each run of the animation will have a duration of `period`. If `period` is not
  /// provided, [duration] will be used instead, which has to be set before [repeat] is
  /// called either in the constructor or later by using the [duration] setter.
  ///
  /// If a value is passed to [count], the animation will perform that many
  /// iterations before stopping. Otherwise, the animation repeats indefinitely.
  ///
  /// Returns a [TickerFuture] that never completes, unless a [count] is specified.
  /// The [TickerFuture.orCancel] future completes with an error when the animation is
  /// stopped (e.g. with [stop]).
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  /// 开始向前运行此动画,并在完成时重新启动动画。
  ///
  /// 当没有为 [min] 和 [max] 设置明确值时,默认在 [AnimationController] 的
  /// [lowerBound] 和 [upperBound] 之间重复。
  ///
  /// 当 [reverse] 设置为 true 时,起始值将在每次重复时在 [min] 和 [max] 值之间交替,
  /// 而不是总是从 [min] 开始。当动画从 [max] 运行到 [min] 时,
  /// [status] 将报告为 [AnimationStatus.reverse]。
  ///
  /// 动画的每次运行将具有 `period` 的持续时间。如果未提供 `period`,
  /// 将使用 [duration] 代替,必须在调用 [repeat] 之前设置,
  /// 无论是在构造函数中还是稍后使用 [duration] setter。
  ///
  /// 如果向 [count] 传递值,动画将在停止前执行那么多次迭代。
  /// 否则,动画将无限重复。
  ///
  /// 返回一个永不完成的 [TickerFuture],除非指定了 [count]。
  /// 当动画停止时(例如使用 [stop]),[TickerFuture.orCancel] future 以错误完成。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  TickerFuture repeat({
    double? min,
    double? max,
    bool reverse = false,
    Duration? period,
    int? count,
  }) {
    min ??= lowerBound;
    max ??= upperBound;
    period ??= duration;
    assert(() {
      if (period == null) {
        throw FlutterError(
          'AnimationController.repeat() called without an explicit period and with no default Duration.\n'
          'Either the "period" argument to the repeat() method should be provided, or the '
          '"duration" property should be set, either in the constructor or later, before '
          'calling the repeat() function.',
        );
      }
      return true;
    }());
    assert(max >= min);
    assert(max <= upperBound && min >= lowerBound);
    assert(count == null || count > 0, 'Count shall be greater than zero if not null');
    stop();
    return _startSimulation(_RepeatingSimulation(_value, min, max, reverse, period!, _directionSetter, count));
  }

  void _directionSetter(_AnimationDirection direction) {
    _direction = direction;
    _status = (_direction == _AnimationDirection.forward) ?
      AnimationStatus.forward :
      AnimationStatus.reverse;
    _checkStatusChanged();
  }

  /// Drives the animation with a spring (within [lowerBound] and [upperBound])
  /// and initial velocity.
  ///
  /// If velocity is positive, the animation will complete, otherwise it will
  /// dismiss. The velocity is specified in units per second. If the
  /// [SemanticsBinding.disableAnimations] flag is set, the velocity is somewhat
  /// arbitrarily multiplied by 200.
  ///
  /// The [springDescription] parameter can be used to specify a custom
  /// [SpringType.criticallyDamped] or [SpringType.overDamped] spring with which
  /// to drive the animation. By default, a [SpringType.criticallyDamped] spring
  /// is used. See [SpringDescription.withDampingRatio] for how to create a
  /// suitable [SpringDescription].
  ///
  /// The resulting spring simulation cannot be of type [SpringType.underDamped];
  /// such a spring would oscillate rather than fling.
  ///
  /// Returns a [TickerFuture] that completes when the animation is complete.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  /// 使用弹簧(在 [lowerBound] 和 [upperBound] 内)和初始速度驱动动画。
  ///
  /// 如果速度为正,动画将完成,否则将解除。速度以每秒单位指定。
  /// 如果设置了 [SemanticsBinding.disableAnimations] 标志,
  /// 速度会被任意地乘以 200。
  ///
  /// [springDescription] 参数可用于指定自定义的 [SpringType.criticallyDamped]
  /// 或 [SpringType.overDamped] 弹簧来驱动动画。默认情况下,
  /// 使用 [SpringType.criticallyDamped] 弹簧。
  /// 请参阅 [SpringDescription.withDampingRatio] 了解如何创建合适的 [SpringDescription]。
  ///
  /// 生成的弹簧模拟不能是 [SpringType.underDamped] 类型;
  /// 这样的弹簧会振荡而不是抛掷。
  ///
  /// 返回一个在动画完成时完成的 [TickerFuture]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  TickerFuture fling({ double velocity = 1.0, SpringDescription? springDescription, AnimationBehavior? animationBehavior }) {
    springDescription ??= _kFlingSpringDescription;
    _direction = velocity < 0.0 ? _AnimationDirection.reverse : _AnimationDirection.forward;
    final double target = velocity < 0.0 ? lowerBound - _kFlingTolerance.distance
                                         : upperBound + _kFlingTolerance.distance;
    final AnimationBehavior behavior = animationBehavior ?? this.animationBehavior;
    final double scale = switch (behavior) {
      // This is arbitrary (it was chosen because it worked for the drawer widget).
      AnimationBehavior.normal when SemanticsBinding.instance.disableAnimations => 200.0,
      AnimationBehavior.normal || AnimationBehavior.preserve => 1.0,
    };
    final SpringSimulation simulation = SpringSimulation(springDescription, value, target, velocity * scale)
      ..tolerance = _kFlingTolerance;
    assert(
      simulation.type != SpringType.underDamped,
      'The specified spring simulation is of type SpringType.underDamped.\n'
      'An underdamped spring results in oscillation rather than a fling. '
      'Consider specifying a different springDescription, or use animateWith() '
      'with an explicit SpringSimulation if an underdamped spring is intentional.',
    );
    stop();
    return _startSimulation(simulation);
  }

  /// Drives the animation according to the given simulation.
  ///
  /// The values from the simulation are clamped to the [lowerBound] and
  /// [upperBound]. To avoid this, consider creating the [AnimationController]
  /// using the [AnimationController.unbounded] constructor.
  ///
  /// Returns a [TickerFuture] that completes when the animation is complete.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  ///
  /// The [status] is always [AnimationStatus.forward] for the entire duration
  /// of the simulation.
  /// 根据给定的模拟驱动动画。
  ///
  /// 来自模拟的值被限制在 [lowerBound] 和 [upperBound] 之间。
  /// 要避免这种情况,请考虑使用 [AnimationController.unbounded] 构造函数
  /// 创建 [AnimationController]。
  ///
  /// 返回一个在动画完成时完成的 [TickerFuture]。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  ///
  /// 在整个模拟持续时间内,[status] 始终为 [AnimationStatus.forward]。
  TickerFuture animateWith(Simulation simulation) {
    assert(
      _ticker != null,
      'AnimationController.animateWith() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    stop();
    _direction = _AnimationDirection.forward;
    return _startSimulation(simulation);
  }

  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;
  }

  /// Stops running this animation.
  ///
  /// This does not trigger any notifications. The animation stops in its
  /// current state.
  ///
  /// By default, the most recently returned [TickerFuture] is marked as having
  /// been canceled, meaning the future never completes and its
  /// [TickerFuture.orCancel] derivative future completes with a [TickerCanceled]
  /// error. By passing the `canceled` argument with the value false, this is
  /// reversed, and the futures complete successfully.
  ///
  /// See also:
  ///
  ///  * [reset], which stops the animation and resets it to the [lowerBound],
  ///    and which does send notifications.
  ///  * [forward], [reverse], [animateTo], [animateWith], [fling], and [repeat],
  ///    which restart the animation controller.
  /// 停止运行此动画。
  ///
  /// 这不会触发任何通知。动画在其当前状态下停止。
  ///
  /// 默认情况下,最近返回的 [TickerFuture] 被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  /// 通过传递值为 false 的 `canceled` 参数,这种情况会被逆转,
  /// future 会成功完成。
  ///
  /// 另请参阅:
  ///
  ///  * [reset],停止动画并将其重置为 [lowerBound],并发送通知。
  ///  * [forward]、[reverse]、[animateTo]、[animateWith]、[fling] 和 [repeat],
  ///    重新启动动画控制器。
  void stop({ bool canceled = true }) {
    assert(
      _ticker != null,
      'AnimationController.stop() called after AnimationController.dispose()\n'
      'AnimationController methods should not be used after calling dispose.',
    );
    _simulation = null;
    _lastElapsedDuration = null;
    _ticker!.stop(canceled: canceled);
  }

  /// Release the resources used by this object. The object is no longer usable
  /// after this method is called.
  ///
  /// The most recently returned [TickerFuture], if any, is marked as having been
  /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
  /// derivative future completes with a [TickerCanceled] error.
  /// 释放此对象使用的资源。调用此方法后,对象不再可用。
  ///
  /// 最近返回的 [TickerFuture](如果有)被标记为已取消,
  /// 意味着 future 永远不会完成,其 [TickerFuture.orCancel]
  /// 派生 future 以 [TickerCanceled] 错误完成。
  @override
  void dispose() {
    assert(() {
      if (_ticker == null) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('AnimationController.dispose() called more than once.'),
          ErrorDescription('A given $runtimeType cannot be disposed more than once.\n'),
          DiagnosticsProperty<AnimationController>(
            'The following $runtimeType object was disposed multiple times',
            this,
            style: DiagnosticsTreeStyle.errorProperty,
          ),
        ]);
      }
      return true;
    }());
    if (kFlutterMemoryAllocationsEnabled) {
      FlutterMemoryAllocations.instance.dispatchObjectDisposed(object: this);
    }
    _ticker!.dispose();
    _ticker = null;
    clearStatusListeners();
    clearListeners();
    super.dispose();
  }

  AnimationStatus _lastReportedStatus = AnimationStatus.dismissed;
  void _checkStatusChanged() {
    final AnimationStatus newStatus = status;
    if (_lastReportedStatus != newStatus) {
      _lastReportedStatus = newStatus;
      notifyStatusListeners(newStatus);
    }
  }

  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();
  }

  @override
  String toStringDetails() {
    final String paused = isAnimating ? '' : '; paused';
    final String ticker = _ticker == null ? '; DISPOSED' : (_ticker!.muted ? '; silenced' : '');
    String label = '';
    assert(() {
      if (debugLabel != null) {
        label = '; for $debugLabel';
      }
      return true;
    }());
    final String more = '${super.toStringDetails()} ${value.toStringAsFixed(3)}';
    return '$more$paused$ticker$label';
  }
}

class _InterpolationSimulation extends Simulation {
  _InterpolationSimulation(this._begin, this._end, Duration duration, this._curve, double scale)
    : assert(duration.inMicroseconds > 0),
      _durationInSeconds = (duration.inMicroseconds * scale) / Duration.microsecondsPerSecond;

  final double _durationInSeconds;
  final double _begin;
  final double _end;
  final Curve _curve;

  @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
  double dx(double timeInSeconds) {
    final double epsilon = tolerance.time;
    return (x(timeInSeconds + epsilon) - x(timeInSeconds - epsilon)) / (2 * epsilon);
  }

  @override
  bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
}

typedef _DirectionSetter = void Function(_AnimationDirection direction);

class _RepeatingSimulation extends Simulation {
  _RepeatingSimulation(
    double initialValue,
    this.min,
    this.max,
    this.reverse,
    Duration period,
    this.directionSetter,
    this.count,
  )  : assert(
          count == null || count > 0,
          'Count shall be greater than zero if not null',
        ),
        _periodInSeconds = period.inMicroseconds / Duration.microsecondsPerSecond,
        _initialT = (max == min) ? 0.0 : ((clampDouble(initialValue, min, max) - min) / (max - min)) * (period.inMicroseconds / Duration.microsecondsPerSecond) {
    assert(_periodInSeconds > 0.0);
    assert(_initialT >= 0.0);
  }

  final double min;
  final double max;
  final bool reverse;
  final int? count;
  final _DirectionSetter directionSetter;

  final double _periodInSeconds;
  final double _initialT;

  late final double _exitTimeInSeconds = (count! * _periodInSeconds) - _initialT;

  @override
  double x(double timeInSeconds) {
    assert(timeInSeconds >= 0.0);

    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
  double dx(double timeInSeconds) => (max - min) / _periodInSeconds;

  @override
  bool isDone(double timeInSeconds) {
    // if [timeInSeconds] elapsed the [_exitTimeInSeconds] && [count] is not null,
    // consider marking the simulation as "DONE"
    return count != null && (timeInSeconds >= _exitTimeInSeconds);
  }
}
相关推荐
卸任3 小时前
Vibe coding工具使用心得
前端·ai编程
brien94 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance4 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M4 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写5 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李5 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易5 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马5 小时前
前沿视界
前端
kyriewen5 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易5 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员