Skip to content

Commit

Permalink
Add directionality to all adpaters
Browse files Browse the repository at this point in the history
  • Loading branch information
gskinner committed Feb 7, 2023
1 parent 39e8c8d commit 599258e
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 37 deletions.
2 changes: 1 addition & 1 deletion example/lib/examples/adapter_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class AdapterView extends StatelessWidget {
.animate(
adapter: ScrollAdapter(
scrollController,
animated: true, // smooth the inputs
animated: true, // smooth the scroll input
),
)
.scaleXY(end: 0.5, curve: Curves.easeIn)
Expand Down
44 changes: 28 additions & 16 deletions lib/src/adapters/adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,29 @@ import 'package:flutter/widgets.dart';
/// an animation with a slider input, or progressing an animation based on
/// the time of day.
///
/// Adapters must expose an `attach` method which accepts the
/// [animated] specifies that the adapter should animate to new values. If `false`, it
/// will jump to the new value, if `true` it will animate to the value using a
/// duration calculated from the animation's total duration and the value change.
/// Defaults to `false`.
///
/// Setting [direction] to [Direction.forward] or [Direction.reverse] will cause
/// the adapter to only update if the new value is greater than or less than the
/// current value respectively.
///
/// Adapter implementations must expose an [attach] method which accepts the
/// [AnimationController] used by an [Animate] instance, and adds the logic
/// to drive it from an external source by updating its `value` (0-1). See the
/// included adapters for implementation examples.
abstract class Adapter {
Adapter({bool? animated}) : animated = animated ?? false;
Adapter({bool? animated, this.direction}) : animated = animated ?? false;

/// Indicates whether the adapter should animate to new values. If `false`, it
/// will jump to the new value, if `true` it will animate to the value using a
/// duration calculated from the animation's total duration and the value change.
/// Defaults to `false`.
final bool animated;

AnimationController? _controller;
final Direction? direction;

// properties to support animated:
AnimationController? _controller;
Ticker? _ticker;
double _target = 0;
double _value = 0;
int _prevT = 0;

// this is called by Animate to associate the AnimationController.
Expand All @@ -43,19 +48,24 @@ abstract class Adapter {
void config(AnimationController controller, double value) {
assert(_controller == null, 'An adapter was assigned twice.');
_controller = controller;
_controller?.value = value;
_controller?.value = _value = value;
_ticker = Ticker(_tick);
}

// called by implementers to update the value.
// called by implementers to update the value. Manages direction and animated.
void updateValue(double value) {
AnimationController controller = _controller!;
if (_value == value ||
(direction == Direction.forward && value < _value) ||
(direction == Direction.reverse && value > _value)) {
return;
}
_value = value;

if (!animated) {
controller.value = value;
} else if (value != controller.value) {
Ticker ticker = _ticker!;
_target = value;
_prevT = DateTime.now().microsecondsSinceEpoch;
if (!ticker.isActive) ticker.start();
}
Expand All @@ -71,14 +81,16 @@ abstract class Adapter {
double d = (t - _prevT) / controller.duration!.inMicroseconds;
double val = controller.value;

if (val < _target) {
val = min(_target, val + d);
if (val < _value) {
val = min(_value, val + d);
} else {
val = max(_target, val - d);
val = max(_value, val - d);
}

_prevT = t;
controller.value = val;
if (val == _target) _ticker!.stop();
if (val == _value) _ticker!.stop();
}
}

enum Direction { forward, reverse }
10 changes: 8 additions & 2 deletions lib/src/adapters/change_notifier_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import '../../flutter_animate.dart';

/// Drives an [Animate] animation from a [ChangeNotifier]. The [valueGetter]
/// should provide a value in the range `0-1` when a change occurs.
///
/// See [Adapter] for information on [direction] and [animated].
class ChangeNotifierAdapter extends Adapter {
ChangeNotifierAdapter(this.notifier, this.valueGetter, {bool? animated})
: super(animated: animated);
ChangeNotifierAdapter(
this.notifier,
this.valueGetter, {
bool? animated,
Direction? direction,
}) : super(animated: animated, direction: direction);

final ChangeNotifier notifier;
final ValueGetter<double> valueGetter;
Expand Down
22 changes: 8 additions & 14 deletions lib/src/adapters/scroll_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,28 @@ import '../../flutter_animate.dart';
/// )
/// ).fadeIn().slide();
/// ```
///
/// See [Adapter] for information on [direction] and [animated].
class ScrollAdapter extends Adapter {
ScrollAdapter(
this.scrollController, {
this.begin,
this.end,
this.direction,
bool? animated,
}) : super(animated: animated);
Direction? direction,
}) : super(animated: animated, direction: direction);

final ScrollController scrollController;
final double? begin;
final double? end;
final ScrollDirection? direction;
double? _value;

@override
void attach(AnimationController controller) {
_value = _getValue();
config(controller, _value ?? 0);
config(controller, _getValue() ?? 0);
scrollController.addListener(() {
double? old = _value, val = _getValue();
if (val == null || old == val) return; // no clients or no change
if (old == null ||
direction == null ||
(direction == ScrollDirection.forward && val > old) ||
(direction == ScrollDirection.reverse && val < old)) {
updateValue(_value = val);
}
double? value = _getValue();
if (value == null) return; // no clients
updateValue(value);
});
}

Expand Down
10 changes: 8 additions & 2 deletions lib/src/adapters/value_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ import '../../flutter_animate.dart';
/// Text("Hello").animate(adapter: ValueAdapter(_sliderVal))
/// .fadeIn().slide();
/// ```
///
/// See [Adapter] for information on [direction] and [animated].
class ValueAdapter extends ValueNotifierAdapter {
ValueAdapter(double value, {bool? animated})
: super(ValueNotifier<double>(value), animated: animated);
ValueAdapter(double value, {bool? animated, Direction? direction})
: super(
ValueNotifier<double>(value),
animated: animated,
direction: direction,
);

set value(double value) => notifier.value = value;
}
6 changes: 4 additions & 2 deletions lib/src/adapters/value_notifier_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import '../../flutter_animate.dart';

/// Drives an [Animate] animation from a [ValueNotifier]. The value from the
/// notifier should be in the range `0-1`.
///
/// See [Adapter] for information on [direction] and [animated].
class ValueNotifierAdapter extends Adapter {
ValueNotifierAdapter(this.notifier, {bool? animated})
: super(animated: animated);
ValueNotifierAdapter(this.notifier, {bool? animated, Direction? direction})
: super(animated: animated, direction: direction);

final ValueNotifier<double> notifier;

Expand Down

0 comments on commit 599258e

Please sign in to comment.