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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com