1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:ui' show Color, Rect, Size;
6
7import 'package:flutter/foundation.dart';
8
9import 'animations.dart';
10
11export 'dart:ui' show Color, Rect, Size;
12
13export 'animation.dart' show Animation;
14export 'curves.dart' show Curve;
15
16// Examples can assume:
17// late Animation _animation;
18// late AnimationController _controller;
19
20/// A typedef used by [Animatable.fromCallback] to create an [Animatable]
21/// from a callback.
22typedef AnimatableCallback<T> = T Function(double value);
23
24/// An object that can produce a value of type `T` given an [Animation<double>]
25/// as input.
26///
27/// Typically, the values of the input animation are nominally in the range 0.0
28/// to 1.0. In principle, however, any value could be provided.
29///
30/// The main subclass of [Animatable] is [Tween].
31abstract class Animatable<T> {
32 /// Abstract const constructor. This constructor enables subclasses to provide
33 /// const constructors so that they can be used in const expressions.
34 const Animatable();
35
36 /// Create a new [Animatable] from the provided [callback].
37 ///
38 /// See also:
39 ///
40 /// * [Animation.drive], which provides an example for how this can be
41 /// used.
42 const factory Animatable.fromCallback(AnimatableCallback<T> callback) = _CallbackAnimatable<T>;
43
44 /// Returns the value of the object at point `t`.
45 ///
46 /// The value of `t` is nominally a fraction in the range 0.0 to 1.0, though
47 /// in practice it may extend outside this range.
48 ///
49 /// See also:
50 ///
51 /// * [evaluate], which is a shorthand for applying [transform] to the value
52 /// of an [Animation].
53 /// * [Curve.transform], a similar method for easing curves.
54 T transform(double t);
55
56 /// The current value of this object for the given [Animation].
57 ///
58 /// This function is implemented by deferring to [transform]. Subclasses that
59 /// want to provide custom behavior should override [transform], not
60 /// [evaluate].
61 ///
62 /// See also:
63 ///
64 /// * [transform], which is similar but takes a `t` value directly instead of
65 /// an [Animation].
66 /// * [animate], which creates an [Animation] out of this object, continually
67 /// applying [evaluate].
68 T evaluate(Animation<double> animation) => transform(animation.value);
69
70 /// Returns a new [Animation] that is driven by the given animation but that
71 /// takes on values determined by this object.
72 ///
73 /// Essentially this returns an [Animation] that automatically applies the
74 /// [evaluate] method to the parent's value.
75 ///
76 /// See also:
77 ///
78 /// * [AnimationController.drive], which does the same thing from the
79 /// opposite starting point.
80 Animation<T> animate(Animation<double> parent) {
81 return _AnimatedEvaluation<T>(parent, this);
82 }
83
84 /// Returns a new [Animatable] whose value is determined by first evaluating
85 /// the given parent and then evaluating this object.
86 ///
87 /// This allows [Tween]s to be chained before obtaining an [Animation].
88 Animatable<T> chain(Animatable<double> parent) {
89 return _ChainedEvaluation<T>(parent, this);
90 }
91}
92
93// A concrete subclass of `Animatable` used by `Animatable.fromCallback`.
94class _CallbackAnimatable<T> extends Animatable<T> {
95 const _CallbackAnimatable(this._callback);
96
97 final AnimatableCallback<T> _callback;
98
99 @override
100 T transform(double t) {
101 return _callback(t);
102 }
103}
104
105class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
106 _AnimatedEvaluation(this.parent, this._evaluatable);
107
108 @override
109 final Animation<double> parent;
110
111 final Animatable<T> _evaluatable;
112
113 @override
114 T get value => _evaluatable.evaluate(parent);
115
116 @override
117 String toString() {
118 return '$parent\u27A9$_evaluatable\u27A9$value';
119 }
120
121 @override
122 String toStringDetails() {
123 return '${super.toStringDetails()} $_evaluatable';
124 }
125}
126
127class _ChainedEvaluation<T> extends Animatable<T> {
128 _ChainedEvaluation(this._parent, this._evaluatable);
129
130 final Animatable<double> _parent;
131 final Animatable<T> _evaluatable;
132
133 @override
134 T transform(double t) {
135 return _evaluatable.transform(_parent.transform(t));
136 }
137
138 @override
139 String toString() {
140 return '$_parent\u27A9$_evaluatable';
141 }
142}
143
144/// A linear interpolation between a beginning and ending value.
145///
146/// [Tween] is useful if you want to interpolate across a range.
147///
148/// To use a [Tween] object with an animation, call the [Tween] object's
149/// [animate] method and pass it the [Animation] object that you want to
150/// modify.
151///
152/// You can chain [Tween] objects together using the [chain] method, so that a
153/// single [Animation] object is configured by multiple [Tween] objects called
154/// in succession. This is different than calling the [animate] method twice,
155/// which results in two separate [Animation] objects, each configured with a
156/// single [Tween].
157///
158/// {@tool snippet}
159///
160/// Suppose `_controller` is an [AnimationController], and we want to create an
161/// [Animation<Offset>] that is controlled by that controller, and save it in
162/// `_animation`. Here are two possible ways of expressing this:
163///
164/// ```dart
165/// _animation = _controller.drive(
166/// Tween<Offset>(
167/// begin: const Offset(100.0, 50.0),
168/// end: const Offset(200.0, 300.0),
169/// ),
170/// );
171/// ```
172/// {@end-tool}
173/// {@tool snippet}
174///
175/// ```dart
176/// _animation = Tween<Offset>(
177/// begin: const Offset(100.0, 50.0),
178/// end: const Offset(200.0, 300.0),
179/// ).animate(_controller);
180/// ```
181/// {@end-tool}
182///
183/// In both cases, the `_animation` variable holds an object that, over the
184/// lifetime of the `_controller`'s animation, returns a value
185/// (`_animation.value`) that depicts a point along the line between the two
186/// offsets above. If we used a [MaterialPointArcTween] instead of a
187/// [Tween<Offset>] in the code above, the points would follow a pleasing curve
188/// instead of a straight line, with no other changes necessary.
189///
190/// ## Performance optimizations
191///
192/// Tweens are mutable; specifically, their [begin] and [end] values can be
193/// changed at runtime. An object created with [Animation.drive] using a [Tween]
194/// will immediately honor changes to that underlying [Tween] (though the
195/// listeners will only be triggered if the [Animation] is actively animating).
196/// This can be used to change an animation on the fly without having to
197/// recreate all the objects in the chain from the [AnimationController] to the
198/// final [Tween].
199///
200/// If a [Tween]'s values are never changed, however, a further optimization can
201/// be applied: the object can be stored in a `static final` variable, so that
202/// the exact same instance is used whenever the [Tween] is needed. This is
203/// preferable to creating an identical [Tween] afresh each time a [State.build]
204/// method is called, for example.
205///
206/// ## Types with special considerations
207///
208/// Classes with [lerp] static methods typically have corresponding dedicated
209/// [Tween] subclasses that call that method. For example, [ColorTween] uses
210/// [Color.lerp] to implement the [ColorTween.lerp] method.
211///
212/// Types that define `+` and `-` operators to combine values (`T + T → T` and
213/// `T - T → T`) and an `*` operator to scale by multiplying with a double (`T *
214/// double → T`) can be directly used with `Tween<T>`.
215///
216/// This does not extend to any type with `+`, `-`, and `*` operators. In
217/// particular, [int] does not satisfy this precise contract (`int * double`
218/// actually returns [num], not [int]). There are therefore two specific classes
219/// that can be used to interpolate integers:
220///
221/// * [IntTween], which is an approximation of a linear interpolation (using
222/// [double.round]).
223/// * [StepTween], which uses [double.floor] to ensure that the result is
224/// never greater than it would be using if a `Tween<double>`.
225///
226/// The relevant operators on [Size] also don't fulfill this contract, so
227/// [SizeTween] uses [Size.lerp].
228///
229/// In addition, some of the types that _do_ have suitable `+`, `-`, and `*`
230/// operators still have dedicated [Tween] subclasses that perform the
231/// interpolation in a more specialized manner. One such class is
232/// [MaterialPointArcTween], which is mentioned above. The [AlignmentTween], and
233/// [AlignmentGeometryTween], and [FractionalOffsetTween] are another group of
234/// [Tween]s that use dedicated `lerp` methods instead of merely relying on the
235/// operators (in particular, this allows them to handle null values in a more
236/// useful manner).
237///
238/// ## Nullability
239///
240/// The [begin] and [end] fields are nullable; a [Tween] does not have to
241/// have non-null values specified when it is created.
242///
243/// If `T` is nullable, then [lerp] and [transform] may return null.
244/// This is typically seen in the case where [begin] is null and `t`
245/// is 0.0, or [end] is null and `t` is 1.0, or both are null (at any
246/// `t` value).
247///
248/// If `T` is not nullable, then [begin] and [end] must both be set to
249/// non-null values before using [lerp] or [transform], otherwise they
250/// will throw.
251///
252/// ## Implementing a Tween
253///
254/// To specialize this class for a new type, the subclass should implement
255/// the [lerp] method (and a constructor). The other methods of this class
256/// are all defined in terms of [lerp].
257class Tween<T extends Object?> extends Animatable<T> {
258 /// Creates a tween.
259 ///
260 /// The [begin] and [end] properties must be non-null before the tween is
261 /// first used, but the arguments can be null if the values are going to be
262 /// filled in later.
263 Tween({
264 this.begin,
265 this.end,
266 });
267
268 /// The value this variable has at the beginning of the animation.
269 ///
270 /// See the constructor for details about whether this property may be null
271 /// (it varies from subclass to subclass).
272 T? begin;
273
274 /// The value this variable has at the end of the animation.
275 ///
276 /// See the constructor for details about whether this property may be null
277 /// (it varies from subclass to subclass).
278 T? end;
279
280 /// Returns the value this variable has at the given animation clock value.
281 ///
282 /// The default implementation of this method uses the `+`, `-`, and `*`
283 /// operators on `T`. The [begin] and [end] properties must therefore be
284 /// non-null by the time this method is called.
285 ///
286 /// In general, however, it is possible for this to return null, especially
287 /// when `t`=0.0 and [begin] is null, or `t`=1.0 and [end] is null.
288 @protected
289 T lerp(double t) {
290 assert(begin != null);
291 assert(end != null);
292 assert(() {
293 // Assertions that attempt to catch common cases of tweening types
294 // that do not conform to the Tween requirements.
295 dynamic result;
296 try {
297 // ignore: avoid_dynamic_calls
298 result = (begin as dynamic) + ((end as dynamic) - (begin as dynamic)) * t;
299 result as T;
300 return true;
301 } on NoSuchMethodError {
302 throw FlutterError.fromParts(<DiagnosticsNode>[
303 ErrorSummary('Cannot lerp between "$begin" and "$end".'),
304 ErrorDescription(
305 'The type ${begin.runtimeType} might not fully implement `+`, `-`, and/or `*`. '
306 'See "Types with special considerations" at https://api.flutter.dev/flutter/animation/Tween-class.html '
307 'for more information.',
308 ),
309 if (begin is Color || end is Color)
310 ErrorHint('To lerp colors, consider ColorTween instead.')
311 else if (begin is Rect || end is Rect)
312 ErrorHint('To lerp rects, consider RectTween instead.')
313 else
314 ErrorHint(
315 'There may be a dedicated "${begin.runtimeType}Tween" for this type, '
316 'or you may need to create one.',
317 ),
318 ]);
319 } on TypeError {
320 throw FlutterError.fromParts(<DiagnosticsNode>[
321 ErrorSummary('Cannot lerp between "$begin" and "$end".'),
322 ErrorDescription(
323 'The type ${begin.runtimeType} returned a ${result.runtimeType} after '
324 'multiplication with a double value. '
325 'See "Types with special considerations" at https://api.flutter.dev/flutter/animation/Tween-class.html '
326 'for more information.',
327 ),
328 if (begin is int || end is int)
329 ErrorHint('To lerp int values, consider IntTween or StepTween instead.')
330 else
331 ErrorHint(
332 'There may be a dedicated "${begin.runtimeType}Tween" for this type, '
333 'or you may need to create one.',
334 ),
335 ]);
336 }
337 }());
338 // ignore: avoid_dynamic_calls
339 return (begin as dynamic) + ((end as dynamic) - (begin as dynamic)) * t as T;
340 }
341
342 /// Returns the interpolated value for the current value of the given animation.
343 ///
344 /// This method returns `begin` and `end` when the animation values are 0.0 or
345 /// 1.0, respectively.
346 ///
347 /// This function is implemented by deferring to [lerp]. Subclasses that want
348 /// to provide custom behavior should override [lerp], not [transform] (nor
349 /// [evaluate]).
350 ///
351 /// See the constructor for details about whether the [begin] and [end]
352 /// properties may be null when this is called. It varies from subclass to
353 /// subclass.
354 @override
355 T transform(double t) {
356 if (t == 0.0) {
357 return begin as T;
358 }
359 if (t == 1.0) {
360 return end as T;
361 }
362 return lerp(t);
363 }
364
365 @override
366 String toString() => '${objectRuntimeType(this, 'Animatable')}($begin \u2192 $end)';
367}
368
369/// A [Tween] that evaluates its [parent] in reverse.
370class ReverseTween<T extends Object?> extends Tween<T> {
371 /// Construct a [Tween] that evaluates its [parent] in reverse.
372 ReverseTween(this.parent)
373 : super(begin: parent.end, end: parent.begin);
374
375 /// This tween's value is the same as the parent's value evaluated in reverse.
376 ///
377 /// This tween's [begin] is the parent's [end] and its [end] is the parent's
378 /// [begin]. The [lerp] method returns `parent.lerp(1.0 - t)` and its
379 /// [evaluate] method is similar.
380 final Tween<T> parent;
381
382 @override
383 T lerp(double t) => parent.lerp(1.0 - t);
384}
385
386/// An interpolation between two colors.
387///
388/// This class specializes the interpolation of [Tween<Color>] to use
389/// [Color.lerp].
390///
391/// The values can be null, representing no color (which is distinct to
392/// transparent black, as represented by [Colors.transparent]).
393///
394/// See [Tween] for a discussion on how to use interpolation objects.
395class ColorTween extends Tween<Color?> {
396 /// Creates a [Color] tween.
397 ///
398 /// The [begin] and [end] properties may be null; the null value
399 /// is treated as transparent.
400 ///
401 /// We recommend that you do not pass [Colors.transparent] as [begin]
402 /// or [end] if you want the effect of fading in or out of transparent.
403 /// Instead prefer null. [Colors.transparent] refers to black transparent and
404 /// thus will fade out of or into black which is likely unwanted.
405 ColorTween({ super.begin, super.end });
406
407 /// Returns the value this variable has at the given animation clock value.
408 @override
409 Color? lerp(double t) => Color.lerp(begin, end, t);
410}
411
412/// An interpolation between two sizes.
413///
414/// This class specializes the interpolation of [Tween<Size>] to use
415/// [Size.lerp].
416///
417/// The values can be null, representing [Size.zero].
418///
419/// See [Tween] for a discussion on how to use interpolation objects.
420class SizeTween extends Tween<Size?> {
421 /// Creates a [Size] tween.
422 ///
423 /// The [begin] and [end] properties may be null; the null value
424 /// is treated as an empty size.
425 SizeTween({ super.begin, super.end });
426
427 /// Returns the value this variable has at the given animation clock value.
428 @override
429 Size? lerp(double t) => Size.lerp(begin, end, t);
430}
431
432/// An interpolation between two rectangles.
433///
434/// This class specializes the interpolation of [Tween<Rect>] to use
435/// [Rect.lerp].
436///
437/// The values can be null, representing a zero-sized rectangle at the
438/// origin ([Rect.zero]).
439///
440/// See [Tween] for a discussion on how to use interpolation objects.
441class RectTween extends Tween<Rect?> {
442 /// Creates a [Rect] tween.
443 ///
444 /// The [begin] and [end] properties may be null; the null value
445 /// is treated as an empty rect at the top left corner.
446 RectTween({ super.begin, super.end });
447
448 /// Returns the value this variable has at the given animation clock value.
449 @override
450 Rect? lerp(double t) => Rect.lerp(begin, end, t);
451}
452
453/// An interpolation between two integers that rounds.
454///
455/// This class specializes the interpolation of [Tween<int>] to be
456/// appropriate for integers by interpolating between the given begin
457/// and end values and then rounding the result to the nearest
458/// integer.
459///
460/// This is the closest approximation to a linear tween that is possible with an
461/// integer. Compare to [StepTween] and [Tween<double>].
462///
463/// The [begin] and [end] values must be set to non-null values before
464/// calling [lerp] or [transform].
465///
466/// See [Tween] for a discussion on how to use interpolation objects.
467class IntTween extends Tween<int> {
468 /// Creates an int tween.
469 ///
470 /// The [begin] and [end] properties must be non-null before the tween is
471 /// first used, but the arguments can be null if the values are going to be
472 /// filled in later.
473 IntTween({ super.begin, super.end });
474
475 // The inherited lerp() function doesn't work with ints because it multiplies
476 // the begin and end types by a double, and int * double returns a double.
477 @override
478 int lerp(double t) => (begin! + (end! - begin!) * t).round();
479}
480
481/// An interpolation between two integers that floors.
482///
483/// This class specializes the interpolation of [Tween<int>] to be
484/// appropriate for integers by interpolating between the given begin
485/// and end values and then using [double.floor] to return the current
486/// integer component, dropping the fractional component.
487///
488/// This results in a value that is never greater than the equivalent
489/// value from a linear double interpolation. Compare to [IntTween].
490///
491/// The [begin] and [end] values must be set to non-null values before
492/// calling [lerp] or [transform].
493///
494/// See [Tween] for a discussion on how to use interpolation objects.
495class StepTween extends Tween<int> {
496 /// Creates an [int] tween that floors.
497 ///
498 /// The [begin] and [end] properties must be non-null before the tween is
499 /// first used, but the arguments can be null if the values are going to be
500 /// filled in later.
501 StepTween({ super.begin, super.end });
502
503 // The inherited lerp() function doesn't work with ints because it multiplies
504 // the begin and end types by a double, and int * double returns a double.
505 @override
506 int lerp(double t) => (begin! + (end! - begin!) * t).floor();
507}
508
509/// A tween with a constant value.
510class ConstantTween<T> extends Tween<T> {
511 /// Create a tween whose [begin] and [end] values equal [value].
512 ConstantTween(T value) : super(begin: value, end: value);
513
514 /// This tween doesn't interpolate, it always returns the same value.
515 @override
516 T lerp(double t) => begin as T;
517
518 @override
519 String toString() => '${objectRuntimeType(this, 'ConstantTween')}(value: $begin)';
520}
521
522/// Transforms the value of the given animation by the given curve.
523///
524/// This class differs from [CurvedAnimation] in that [CurvedAnimation] applies
525/// a curve to an existing [Animation] object whereas [CurveTween] can be
526/// chained with another [Tween] prior to receiving the underlying [Animation].
527/// ([CurvedAnimation] also has the additional ability of having different
528/// curves when the animation is going forward vs when it is going backward,
529/// which can be useful in some scenarios.)
530///
531/// {@tool snippet}
532///
533/// The following code snippet shows how you can apply a curve to a linear
534/// animation produced by an [AnimationController] `controller`:
535///
536/// ```dart
537/// final Animation<double> animation = _controller.drive(
538/// CurveTween(curve: Curves.ease),
539/// );
540/// ```
541/// {@end-tool}
542///
543/// See also:
544///
545/// * [CurvedAnimation], for an alternative way of expressing the sample above.
546/// * [AnimationController], for examples of creating and disposing of an
547/// [AnimationController].
548class CurveTween extends Animatable<double> {
549 /// Creates a curve tween.
550 CurveTween({ required this.curve });
551
552 /// The curve to use when transforming the value of the animation.
553 Curve curve;
554
555 @override
556 double transform(double t) {
557 if (t == 0.0 || t == 1.0) {
558 assert(curve.transform(t).round() == t);
559 return t;
560 }
561 return curve.transform(t);
562 }
563
564 @override
565 String toString() => '${objectRuntimeType(this, 'CurveTween')}(curve: $curve)';
566}
567