Unverified Commit 8f0981c0 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[framework] Animatable.fromCallback and code snippet (#108661)

parent efbdb404
......@@ -216,6 +216,23 @@ abstract class Animation<T> extends Listenable implements ValueListenable<T> {
/// ));
/// ```
/// {@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.
///
/// ```dart
/// Animation<Color> _offset1 = Animation<double>.fromValueListenable(_scrollPosition)
/// .drive(Animatable<Color>.fromCallback((double value) {
/// return Color.fromRGBO(value.round() % 255, 0, 0, 1);
/// }));
/// ```
///
/// {@end-tool}
///
/// See also:
///
......@@ -224,6 +241,8 @@ abstract class Animation<T> extends Listenable implements ValueListenable<T> {
/// * [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.
@optionalTypeArgs
Animation<U> drive<U>(Animatable<U> child) {
assert(this is Animation<double>);
......@@ -266,8 +285,9 @@ abstract class Animation<T> extends Listenable implements ValueListenable<T> {
// 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;
_ValueListenableDelegateAnimation(this._listenable, {
ValueListenableTransformer<T>? transformer,
}) : _transformer = transformer;
final ValueListenable<T> _listenable;
final ValueListenableTransformer<T>? _transformer;
......
......@@ -17,6 +17,10 @@ export 'curves.dart' show Curve;
// late Animation<Offset> _animation;
// late AnimationController _controller;
/// A typedef used by [Animatable.fromCallback] to create an [Animatable]
/// from a callback.
typedef AnimatableCallback<T> = T Function(double);
/// An object that can produce a value of type `T` given an [Animation<double>]
/// as input.
///
......@@ -29,6 +33,14 @@ abstract class Animatable<T> {
/// const constructors so that they can be used in const expressions.
const Animatable();
/// Create a new [Animatable] from the provided [callback].
///
/// See also:
///
/// * [Animation.drive], which provides an example for how this can be
/// used.
const factory Animatable.fromCallback(AnimatableCallback<T> callback) = _CallbackAnimatable<T>;
/// Returns the value of the object at point `t`.
///
/// The value of `t` is nominally a fraction in the range 0.0 to 1.0, though
......@@ -78,6 +90,18 @@ abstract class Animatable<T> {
}
}
// A concrete subclass of `Animatable` used by `Animatable.fromCallback`.
class _CallbackAnimatable<T> extends Animatable<T> {
const _CallbackAnimatable(this._callback);
final AnimatableCallback<T> _callback;
@override
T transform(double t) {
return _callback(t);
}
}
class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
_AnimatedEvaluation(this.parent, this._evaluatable);
......
......@@ -48,4 +48,36 @@ void main() {
expect(animation.value, 1.0);
});
test('Animation created from ValueListenable can be transformed via drive', () {
final ValueNotifier<double> listenable = ValueNotifier<double>(0.0);
final Animation<double> animation = Animation<double>.fromValueListenable(listenable);
final Animation<Offset> offset = animation.drive(Animatable<Offset>.fromCallback((double value) {
return Offset(0.0, value);
}));
expect(offset.value, Offset.zero);
expect(offset.status, AnimationStatus.forward);
listenable.value = 10;
expect(offset.value, const Offset(0.0, 10.0));
bool listenerCalled = false;
void listener() {
listenerCalled = true;
}
offset.addListener(listener);
listenable.value = 0.5;
expect(listenerCalled, true);
listenerCalled = false;
offset.removeListener(listener);
listenable.value = 0.2;
expect(listenerCalled, false);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment