Flutter动画框架之AnimationStatus、Animation源码解析(一)

Flutter动画框架有8个文件。核心架构组件分别如下。

1. Animation - 动画抽象基类

animation.dart 是整个框架的核心抽象,定义了动画的基本契约:

  • 值变化监听 :通过 addListener / removeListener 监听动画值的变化
  • 状态监听 :通过 addStatusListener / removeStatusListener 监听动画状态变化
  • 动画状态 :定义了四种状态 - dismissed (开始)、 forward (正向)、 reverse (反向)、 completed (完成)
  • 链式驱动 :通过 drive 方法可以将动画与Tween链接,实现值的转换

2. AnimationController - 动画控制器

animation_controller.dart 是动画的核心控制器,继承自 Animation :

  • Ticker驱动 :基于 TickerProvider 创建 Ticker ,与屏幕刷新率同步
  • 时间控制 :支持设置 duration 和 reverseDuration
  • 方向控制 :内部维护 _AnimationDirection 枚举控制动画方向
  • 核心方法 :
    • forward() - 正向播放动画
    • reverse() - 反向播放动画
    • animateTo() - 动画到指定值
    • reset() - 重置到初始状态
    • stop() - 停止动画

3. Tween - 值插值器

tween.dart 实现了值的线性插值转换:

  • 泛型设计 :支持任意类型T的插值,只要实现了 + 、 - 、 * 运算符
  • 核心方法 :
    • lerp(double t) - 线性插值核心算法: begin + (end - begin) * t
    • transform(double t) - 处理边界情况(t=0.0返回begin,t=1.0返回end)
    • animate(Animation parent) - 与动画绑定
    • chain(Animatable parent) - 链式组合

4. Curve - 缓动曲线

curves.dart 提供非线性的动画缓动效果:

  • 参数化曲线 :继承自 ParametricCurve

  • 单位区间映射 :将[0,1]区间映射到[0,1]区间,但改变速度曲线

  • 内置曲线 :提供丰富的预定义曲线如 linear 、 easeIn 、 bounceOut 等

以下是抽象类Animation的源码

csharp 复制代码
// 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/scheduler.dart';
/// @docImport 'package:flutter/widgets.dart';
library;

import 'package:flutter/foundation.dart';

import 'tween.dart';

export 'dart:ui' show VoidCallback;

export 'tween.dart' show Animatable;

// Examples can assume:
// late AnimationController _controller;
// late ValueNotifier<double> _scrollPosition;

/// The status of an animation.
/// 动画的状态。
enum AnimationStatus {
  /// The animation is stopped at the beginning.
  /// 动画停止在开始位置。
  dismissed,

  /// The animation is running from beginning to end.
  /// 动画从开始位置向结束位置运行。
  forward,

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

  /// The animation is stopped at the end.
  /// 动画停止在结束位置。
  completed;

  /// Whether the animation is stopped at the beginning.
  /// 动画是否停止在开始位置。
  bool get isDismissed => this == dismissed;

  /// Whether the animation is stopped at the end.
  /// 动画是否停止在结束位置。
  bool get isCompleted => this == completed;

  /// Whether the animation is running in either direction.
  /// 动画是否正在任一方向运行。
  bool get isAnimating => switch (this) {
    forward   || reverse   => true,
    completed || dismissed => false,
  };

  /// {@template flutter.animation.AnimationStatus.isForwardOrCompleted}
  /// Whether the current aim of the animation is toward completion.
  ///
  /// Specifically, returns `true` for [AnimationStatus.forward] or
  /// [AnimationStatus.completed], and `false` for
  /// [AnimationStatus.reverse] or [AnimationStatus.dismissed].
  /// {@endtemplate}
  /// 动画的当前目标是否朝向完成。
  ///
  /// 具体来说,对于 [AnimationStatus.forward] 或 [AnimationStatus.completed] 返回 `true`,
  /// 对于 [AnimationStatus.reverse] 或 [AnimationStatus.dismissed] 返回 `false`。
  bool get isForwardOrCompleted => switch (this) {
    forward || completed => true,
    reverse || dismissed => false,
  };
}

/// Signature for listeners attached using [Animation.addStatusListener].
/// 使用 [Animation.addStatusListener] 附加的监听器的签名。
typedef AnimationStatusListener = void Function(AnimationStatus status);

/// Signature for method used to transform values in [Animation.fromValueListenable].
/// 在 [Animation.fromValueListenable] 中用于转换值的方法签名。
typedef ValueListenableTransformer<T> = T Function(T);

/// A value which might change over time, moving forward or backward.
/// 一个可能随时间变化的值,可以向前或向后移动。
///
/// An animation has a [value] (of type [T]) and a [status].
/// The value conceptually lies on some path, and
/// the status indicates how the value is currently moving along the path:
/// forward, backward, or stopped at the end or the beginning.
/// The path may double back on itself
/// (e.g., if the animation uses a curve that bounces),
/// so even when the animation is conceptually moving forward
/// the value might not change monotonically.
/// 动画有一个 [value](类型为 [T])和一个 [status]。
/// 值在概念上位于某条路径上,状态指示值当前如何沿着路径移动:
/// 向前、向后,或停止在结束或开始位置。
/// 路径可能会折返(例如,如果动画使用弹跳曲线),
/// 所以即使动画在概念上向前移动,值也可能不会单调变化。
///
/// Consumers of the animation can listen for changes to either the value
/// or the status, with [addListener] and [addStatusListener].
/// The listener callbacks are called during the "animation" phase of
/// the pipeline, just prior to rebuilding widgets.
/// 动画的消费者可以使用 [addListener] 和 [addStatusListener] 监听值或状态的变化。
/// 监听器回调在管道的"动画"阶段被调用,就在重建组件之前。
///
/// An animation might move forward or backward on its own as time passes
/// (like the opacity of a button that fades over a fixed duration
/// once the user touches it),
/// or it might be driven by the user
/// (like the position of a slider that the user can drag back and forth),
/// or it might do both
/// (like a switch that snaps into place when released,
/// or a [Dismissible] that responds to drag and fling gestures, etc.).
/// The behavior is normally controlled by method calls on
/// some underlying [AnimationController].
/// When an animation is actively animating, it typically updates on
/// each frame, driven by a [Ticker].
/// 动画可能随着时间的推移自行向前或向后移动
/// (如用户触摸后按钮在固定持续时间内淡出的不透明度),
/// 或者可能由用户驱动
/// (如用户可以来回拖动的滑块位置),
/// 或者两者兼而有之
/// (如释放时卡入位置的开关,或响应拖拽和滑动手势的 [Dismissible] 等)。
/// 行为通常由底层 [AnimationController] 上的方法调用控制。
/// 当动画正在活跃地动画时,它通常在每一帧更新,由 [Ticker] 驱动。
///
/// ## Using animations
/// ## 使用动画
///
/// For simple animation effects, consider using one of the
/// [ImplicitlyAnimatedWidget] subclasses,
/// like [AnimatedScale], [AnimatedOpacity], and many others.
/// When an [ImplicitlyAnimatedWidget] suffices, there is
/// no need to work with [Animation] or the rest of the classes
/// discussed in this section.
/// 对于简单的动画效果,考虑使用 [ImplicitlyAnimatedWidget] 的子类之一,
/// 如 [AnimatedScale]、[AnimatedOpacity] 等等。
/// 当 [ImplicitlyAnimatedWidget] 足够时,就不需要使用 [Animation] 或本节讨论的其他类。
///
/// Otherwise, typically an animation originates with an [AnimationController]
/// (which is itself an [Animation<double>])
/// created by a [State] that implements [TickerProvider].
/// Further animations might be derived from that animation
/// by using e.g. [Tween] or [CurvedAnimation].
/// The animations might be used to configure an [AnimatedWidget]
/// (using one of its many subclasses like [FadeTransition]),
/// or their values might be used directly.
/// 否则,通常动画起源于由实现 [TickerProvider] 的 [State] 创建的 [AnimationController]
/// (它本身就是一个 [Animation<double>])。
/// 可以通过使用例如 [Tween] 或 [CurvedAnimation] 从该动画派生出更多动画。
/// 这些动画可能用于配置 [AnimatedWidget](使用其众多子类之一,如 [FadeTransition]),
/// 或者直接使用它们的值。
///
/// For example, the [AnimationController] may represent
/// the abstract progress of the animation from 0.0 to 1.0;
/// then a [CurvedAnimation] might apply an easing curve;
/// and a [SizeTween] and [ColorTween] might each be applied to that
/// to produce an [Animation<Size>] and an [Animation<Color>] that control
/// a widget shrinking and changing color as the animation proceeds.
/// 例如,[AnimationController] 可能表示动画从 0.0 到 1.0 的抽象进度;
/// 然后 [CurvedAnimation] 可能应用缓动曲线;
/// [SizeTween] 和 [ColorTween] 可能分别应用于此,
/// 以产生控制组件在动画进行时收缩和变色的 [Animation<Size>] 和 [Animation<Color>]。
///
/// ## Performance considerations
/// ## 性能考虑
///
/// Because the [Animation] keeps the same identity as the animation proceeds,
/// it provides a convenient way for a [StatefulWidget] that orchestrates
/// a complex animation to communicate the animation's progress to its
/// various child widgets.  Consider having higher-level widgets in the tree
/// pass lower-level widgets the [Animation] itself, rather than its value,
/// in order to avoid rebuilding the higher-level widgets on each frame
/// even while the animation is active.
/// If the leaf widgets also ignore [value] and pass the whole
/// [Animation] object to a render object (like [FadeTransition] does),
/// they too might be able to avoid rebuild and even relayout, so that the
/// only work needed on each frame of the animation is to repaint.
/// 因为 [Animation] 在动画进行时保持相同的身份,
/// 它为编排复杂动画的 [StatefulWidget] 提供了一种便捷的方式
/// 来向其各种子组件传达动画的进度。考虑让树中的高级组件
/// 向低级组件传递 [Animation] 本身,而不是其值,
/// 以避免在动画活跃时在每一帧重建高级组件。
/// 如果叶子组件也忽略 [value] 并将整个 [Animation] 对象
/// 传递给渲染对象(如 [FadeTransition] 所做的),
/// 它们也可能能够避免重建甚至重新布局,
/// 这样动画每一帧所需的唯一工作就是重绘。
///
/// See also:
///
///  * [ImplicitlyAnimatedWidget] and its subclasses, which provide
///    animation effects without the need to manually work with [Animation],
///    [AnimationController], or even [State].
///  * [AnimationController], an animation you can run forward and backward,
///    stop, or set to a specific value.
///  * [Tween], which can be used to convert [Animation<double>]s into
///    other kinds of [Animation]s.
///  * [AnimatedWidget] and its subclasses, which provide animation effects
///    that can be controlled by an [Animation].
///  * [ImplicitlyAnimatedWidget] 及其子类,提供动画效果而无需手动使用 [Animation]、
///    [AnimationController] 或甚至 [State]。
///  * [AnimationController],一个可以向前和向后运行、停止或设置为特定值的动画。
///  * [Tween],可用于将 [Animation<double>] 转换为其他类型的 [Animation]。
///  * [AnimatedWidget] 及其子类,提供可由 [Animation] 控制的动画效果。
abstract class Animation<T> extends Listenable implements ValueListenable<T> {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  /// 抽象常量构造函数。此构造函数使子类能够提供常量构造函数,
  /// 以便它们可以在常量表达式中使用。
  const Animation();

  /// Create a new animation from a [ValueListenable].
  ///
  /// The returned animation will always have an animation status of
  /// [AnimationStatus.forward]. The value of the provided listenable can
  /// be optionally transformed using the [transformer] function.
  /// 从 [ValueListenable] 创建新的动画。
  ///
  /// 返回的动画将始终具有 [AnimationStatus.forward] 的动画状态。
  /// 提供的可监听对象的值可以选择性地使用 [transformer] 函数进行转换。
  ///
  /// {@tool snippet}
  ///
  /// This constructor can be used to replace instances of [ValueListenableBuilder]
  /// widgets with a corresponding animated widget, like a [FadeTransition].
  ///
  /// Before:
  ///
  /// ```dart
  /// Widget build(BuildContext context) {
  ///   return ValueListenableBuilder<double>(
  ///     valueListenable: _scrollPosition,
  ///     builder: (BuildContext context, double value, Widget? child) {
  ///       final double opacity = (value / 1000).clamp(0, 1);
  ///       return Opacity(opacity: opacity, child: child);
  ///     },
  ///     child: const ColoredBox(
  ///       color: Colors.red,
  ///       child: Text('Hello, Animation'),
  ///     ),
  ///   );
  /// }
  /// ```
  ///
  /// {@end-tool}
  /// {@tool snippet}
  ///
  /// After:
  ///
  /// ```dart
  /// Widget build2(BuildContext context) {
  ///   return FadeTransition(
  ///     opacity: Animation<double>.fromValueListenable(_scrollPosition, transformer: (double value) {
  ///       return (value / 1000).clamp(0, 1);
  ///     }),
  ///     child: const ColoredBox(
  ///       color: Colors.red,
  ///       child: Text('Hello, Animation'),
  ///     ),
  ///   );
  /// }
  /// ```
  /// {@end-tool}
  factory Animation.fromValueListenable(ValueListenable<T> listenable, {
    ValueListenableTransformer<T>? transformer,
  }) = _ValueListenableDelegateAnimation<T>;

  // keep these next five dartdocs in sync with the dartdocs in AnimationWithParentMixin<T>

  /// Calls the listener every time the value of the animation changes.
  ///
  /// Listeners can be removed with [removeListener].
  /// 每当动画值发生变化时调用监听器。
  ///
  /// 可以使用 [removeListener] 移除监听器。
  @override
  void addListener(VoidCallback listener);

  /// Stop calling the listener every time the value of the animation changes.
  ///
  /// If `listener` is not currently registered as a listener, this method does
  /// nothing.
  ///
  /// Listeners can be added with [addListener].
  /// 停止在动画值每次变化时调用监听器。
  ///
  /// 如果 `listener` 当前未注册为监听器,此方法不执行任何操作。
  ///
  /// 可以使用 [addListener] 添加监听器。
  @override
  void removeListener(VoidCallback listener);

  /// Calls listener every time the status of the animation changes.
  ///
  /// Listeners can be removed with [removeStatusListener].
  /// 每当动画状态发生变化时调用监听器。
  ///
  /// 可以使用 [removeStatusListener] 移除监听器。
  void addStatusListener(AnimationStatusListener listener);

  /// Stops calling the listener every time the status of the animation changes.
  ///
  /// If `listener` is not currently registered as a status listener, this
  /// method does nothing.
  ///
  /// Listeners can be added with [addStatusListener].
  /// 停止在动画状态每次变化时调用监听器。
  ///
  /// 如果 `listener` 当前未注册为状态监听器,此方法不执行任何操作。
  ///
  /// 可以使用 [addStatusListener] 添加监听器。
  void removeStatusListener(AnimationStatusListener listener);

  /// The current status of this animation.
  /// 此动画的当前状态。
  AnimationStatus get status;

  /// The current value of the animation.
  /// 动画的当前值。
  @override
  T get value;

  /// Whether this animation is stopped at the beginning.
  /// 此动画是否停止在开始位置。
  bool get isDismissed => status.isDismissed;

  /// Whether this animation is stopped at the end.
  /// 此动画是否停止在结束位置。
  bool get isCompleted => status.isCompleted;

  /// Whether this animation is running in either direction.
  ///
  /// By default, this value is equal to `status.isAnimating`, but
  /// [AnimationController] overrides this method so that its output
  /// depends on whether the controller is actively ticking.
  /// 此动画是否正在任一方向运行。
  ///
  /// 默认情况下,此值等于 `status.isAnimating`,但
  /// [AnimationController] 重写了此方法,使其输出
  /// 取决于控制器是否正在活跃地计时。
  bool get isAnimating => status.isAnimating;

  /// {@macro flutter.animation.AnimationStatus.isForwardOrCompleted}
  bool get isForwardOrCompleted => status.isForwardOrCompleted;

  /// Chains a [Tween] (or [CurveTween]) to this [Animation].
  ///
  /// This method is only valid for `Animation<double>` instances (i.e. when `T`
  /// is `double`). This means, for instance, that it can be called on
  /// [AnimationController] objects, as well as [CurvedAnimation]s,
  /// [ProxyAnimation]s, [ReverseAnimation]s, [TrainHoppingAnimation]s, etc.
  ///
  /// It returns an [Animation] specialized to the same type, `U`, as the
  /// argument to the method (`child`), whose value is derived by applying the
  /// given [Tween] to the value of this [Animation].
  /// 将 [Tween](或 [CurveTween])链接到此 [Animation]。
  ///
  /// 此方法仅对 `Animation<double>` 实例有效(即当 `T` 为 `double` 时)。
  /// 这意味着,例如,它可以在 [AnimationController] 对象以及
  /// [CurvedAnimation]、[ProxyAnimation]、[ReverseAnimation]、[TrainHoppingAnimation] 等上调用。
  ///
  /// 它返回一个专门化为与方法参数(`child`)相同类型 `U` 的 [Animation],
  /// 其值通过将给定的 [Tween] 应用于此 [Animation] 的值而得出。
  ///
  /// {@tool snippet}
  ///
  /// Given an [AnimationController] `_controller`, the following code creates
  /// an `Animation<Alignment>` that swings from top left to top right as the
  /// controller goes from 0.0 to 1.0:
  /// 给定一个 [AnimationController] `_controller`,以下代码创建一个
  /// `Animation<Alignment>`,当控制器从 0.0 到 1.0 时,从左上角摆动到右上角:
  ///
  /// ```dart
  /// Animation<Alignment> alignment1 = _controller.drive(
  ///   AlignmentTween(
  ///     begin: Alignment.topLeft,
  ///     end: Alignment.topRight,
  ///   ),
  /// );
  /// ```
  /// {@end-tool}
  /// {@tool snippet}
  ///
  /// The `alignment1.value` could then be used in a widget's build method, for
  /// instance, to position a child using an [Align] widget such that the
  /// position of the child shifts over time from the top left to the top right.
  ///
  /// It is common to ease this kind of curve, e.g. making the transition slower
  /// at the start and faster at the end. The following snippet shows one way to
  /// chain the alignment tween in the previous example to an easing curve (in
  /// this case, [Curves.easeIn]). In this example, the tween is created
  /// elsewhere as a variable that can be reused, since none of its arguments
  /// vary.
  /// 然后可以在 widget 的 build 方法中使用 `alignment1.value`,例如,
  /// 使用 [Align] widget 定位子组件,使子组件的位置随时间从左上角移动到右上角。
  ///
  /// 通常会对这种曲线进行缓动,例如使过渡在开始时较慢,在结束时较快。
  /// 以下代码片段显示了将前面示例中的对齐补间链接到缓动曲线的一种方法
  ///(在这种情况下是 [Curves.easeIn])。在此示例中,补间在其他地方创建为
  /// 可重用的变量,因为其参数都不会变化。
  ///
  /// ```dart
  /// final Animatable<Alignment> tween = AlignmentTween(begin: Alignment.topLeft, end: Alignment.topRight)
  ///   .chain(CurveTween(curve: Curves.easeIn));
  /// // ...
  /// final Animation<Alignment> alignment2 = _controller.drive(tween);
  /// ```
  /// {@end-tool}
  /// {@tool snippet}
  ///
  /// The following code is exactly equivalent, and is typically clearer when
  /// the tweens are created inline, as might be preferred when the tweens have
  /// values that depend on other variables:
  /// 以下代码完全等效,当内联创建补间时通常更清晰,
  /// 当补间具有依赖于其他变量的值时可能更受欢迎:
  ///
  /// ```dart
  /// Animation<Alignment> alignment3 = _controller
  ///   .drive(CurveTween(curve: Curves.easeIn))
  ///   .drive(AlignmentTween(
  ///     begin: Alignment.topLeft,
  ///     end: Alignment.topRight,
  ///   ));
  /// ```
  /// {@end-tool}
  /// {@tool snippet}
  ///
  /// This method can be paired with an [Animatable] created via
  /// [Animatable.fromCallback] in order to transform an animation with a
  /// callback function. This can be useful for performing animations that
  /// do not have well defined start or end points. This example transforms
  /// the current scroll position into a color that cycles through values
  /// of red.
  /// 此方法可以与通过 [Animatable.fromCallback] 创建的 [Animatable] 配对,
  /// 以便使用回调函数转换动画。这对于执行没有明确定义的开始或结束点的动画很有用。
  /// 此示例将当前滚动位置转换为循环红色值的颜色。
  ///
  /// ```dart
  /// Animation<Color> color = Animation<double>.fromValueListenable(_scrollPosition)
  ///   .drive(Animatable<Color>.fromCallback((double value) {
  ///     return Color.fromRGBO(value.round() % 255, 0, 0, 1);
  ///   }));
  /// ```
  ///
  /// {@end-tool}
  ///
  /// See also:
  ///
  ///  * [Animatable.animate], which does the same thing.
  ///  * [AnimationController], which is usually used to drive animations.
  ///  * [CurvedAnimation], an alternative to [CurveTween] for applying easing
  ///    curves, which supports distinct curves in the forward direction and the
  ///    reverse direction.
  ///  * [Animatable.fromCallback], which allows creating an [Animatable] from an
  ///    arbitrary transformation.
  /// 另请参阅:
  ///
  ///  * [Animatable.animate],执行相同的操作。
  ///  * [AnimationController],通常用于驱动动画。
  ///  * [CurvedAnimation],[CurveTween] 的替代方案,用于应用缓动曲线,
  ///    支持前进方向和反向方向的不同曲线。
  ///  * [Animatable.fromCallback],允许从任意转换创建 [Animatable]。
  @optionalTypeArgs
  Animation<U> drive<U>(Animatable<U> child) {
    assert(this is Animation<double>);
    return child.animate(this as Animation<double>);
  }

  @override
  String toString() {
    return '${describeIdentity(this)}(${toStringDetails()})';
  }

  /// Provides a string describing the status of this object, but not including
  /// information about the object itself.
  ///
  /// This function is used by [Animation.toString] so that [Animation]
  /// subclasses can provide additional details while ensuring all [Animation]
  /// subclasses have a consistent [toString] style.
  ///
  /// The result of this function includes an icon describing the status of this
  /// [Animation] object:
  ///
  /// * "&#x25B6;": [AnimationStatus.forward] ([value] increasing)
  /// * "&#x25C0;": [AnimationStatus.reverse] ([value] decreasing)
  /// * "&#x23ED;": [AnimationStatus.completed] ([value] == 1.0)
  /// * "&#x23EE;": [AnimationStatus.dismissed] ([value] == 0.0)
  /// 提供描述此对象状态的字符串,但不包括对象本身的信息。
  ///
  /// 此函数由 [Animation.toString] 使用,以便 [Animation] 子类
  /// 可以提供额外的详细信息,同时确保所有 [Animation] 子类具有一致的 [toString] 样式。
  ///
  /// 此函数的结果包括描述此 [Animation] 对象状态的图标:
  ///
  /// * "&#x25B6;": [AnimationStatus.forward]([value] 递增)
  /// * "&#x25C0;": [AnimationStatus.reverse]([value] 递减)
  /// * "&#x23ED;": [AnimationStatus.completed]([value] == 1.0)
  /// * "&#x23EE;": [AnimationStatus.dismissed]([value] == 0.0)
  String toStringDetails() {
    return switch (status) {
      AnimationStatus.forward   => '\u25B6', // >
      AnimationStatus.reverse   => '\u25C0', // <
      AnimationStatus.completed => '\u23ED', // >>|
      AnimationStatus.dismissed => '\u23EE', // |<<
    };
  }
}

// An implementation of an animation that delegates to a value listenable with a fixed direction.
// 一个委托给具有固定方向的值监听器的动画实现。
class _ValueListenableDelegateAnimation<T> extends Animation<T> {
  _ValueListenableDelegateAnimation(this._listenable, {
    ValueListenableTransformer<T>? transformer,
  }) : _transformer = transformer;

  final ValueListenable<T> _listenable;
  final ValueListenableTransformer<T>? _transformer;

  @override
  void addListener(VoidCallback listener) {
    _listenable.addListener(listener);
  }

  @override
  void addStatusListener(AnimationStatusListener listener) {
    // status will never change.
    // 状态永远不会改变。
  }

  @override
  void removeListener(VoidCallback listener) {
    _listenable.removeListener(listener);
  }

  @override
  void removeStatusListener(AnimationStatusListener listener) {
    // status will never change.
    // 状态永远不会改变。
  }

  @override
  AnimationStatus get status => AnimationStatus.forward;

  @override
  T get value => _transformer?.call(_listenable.value) ?? _listenable.value;
}
相关推荐
yanlele6 分钟前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang45311 分钟前
React移动端开发项目优化
前端·react.js·前端框架
你的人类朋友15 分钟前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
web_Hsir16 分钟前
vue3.2 前端动态分页算法
前端·算法
烛阴1 小时前
WebSocket实时通信入门到实践
前端·javascript
草巾冒小子1 小时前
vue3实战:.ts文件中的interface定义与抛出、其他文件的调用方式
前端·javascript·vue.js
DoraBigHead1 小时前
你写前端按钮,他们扛服务器压力:搞懂后端那些“黑话”!
前端·javascript·架构
Xiaouuuuua2 小时前
一个简单的脚本,让pdf开启夜间模式
java·前端·pdf
@Dream_Chaser2 小时前
uniapp ruoyi-app 中使用checkbox 无法选中问题
前端·javascript·uni-app
深耕AI2 小时前
【教程】在ubuntu安装Edge浏览器
前端·edge