From 053df166c4013e27ca01d4008f627bc6cdb2a7be Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Mon, 13 Jun 2016 11:32:13 -0700 Subject: [PATCH] AnimationController.fling should respect upper and lower bounds (#4533) Instead of flinging to between 0.0 and 1.0, we should adapt the default spring to the controller's upper and lower bounds. Fixes #3545 --- .../src/animation/animation_controller.dart | 6 ++-- .../flutter/lib/src/animation/forces.dart | 13 +++++++ .../animation/animation_controller_test.dart | 35 +++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/animation/animation_controller.dart b/packages/flutter/lib/src/animation/animation_controller.dart index 1f0e8c8cce..bb54bc7249 100644 --- a/packages/flutter/lib/src/animation/animation_controller.dart +++ b/packages/flutter/lib/src/animation/animation_controller.dart @@ -194,10 +194,10 @@ class AnimationController extends Animation } /// Flings the timeline with an optional force (defaults to a critically - /// damped spring) and initial velocity. If velocity is positive, the - /// animation will complete, otherwise it will dismiss. + /// damped spring within [lowerBound] and [upperBound]) and initial velocity. + /// If velocity is positive, the animation will complete, otherwise it will dismiss. Future fling({ double velocity: 1.0, Force force }) { - force ??= kDefaultSpringForce; + force ??= kDefaultSpringForce.copyWith(left: lowerBound, right: upperBound); _direction = velocity < 0.0 ? _AnimationDirection.reverse : _AnimationDirection.forward; return animateWith(force.release(value, velocity)); } diff --git a/packages/flutter/lib/src/animation/forces.dart b/packages/flutter/lib/src/animation/forces.dart index 700f9bfded..c3464425bf 100644 --- a/packages/flutter/lib/src/animation/forces.dart +++ b/packages/flutter/lib/src/animation/forces.dart @@ -33,6 +33,19 @@ class SpringForce extends Force { /// Where to put the spring's resting point when releasing right. final double right; + /// Creates a copy of this spring force but with the given fields replaced with the new values. + SpringForce copyWith({ + SpringDescription spring, + double left, + double right + }) { + return new SpringForce( + spring ?? this.spring, + left: left ?? this.left, + right: right ?? this.right + ); + } + /// How pricely to terminate the simulation. /// /// We overshoot the target by this distance, but stop the simulation when diff --git a/packages/flutter/test/animation/animation_controller_test.dart b/packages/flutter/test/animation/animation_controller_test.dart index 66fbecd895..61387b3d6e 100644 --- a/packages/flutter/test/animation/animation_controller_test.dart +++ b/packages/flutter/test/animation/animation_controller_test.dart @@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart'; import 'package:test/test.dart'; void main() { - test("Can set value during status callback", () { + test('Can set value during status callback', () { WidgetsFlutterBinding.ensureInitialized(); AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 100) @@ -39,7 +39,7 @@ void main() { controller.stop(); }); - test("Receives status callbacks for forward and reverse", () { + test('Receives status callbacks for forward and reverse', () { WidgetsFlutterBinding.ensureInitialized(); AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 100) @@ -102,7 +102,7 @@ void main() { controller.stop(); }); - test("Forward and reverse from values", () { + test('Forward and reverse from values', () { WidgetsFlutterBinding.ensureInitialized(); AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 100) @@ -129,4 +129,33 @@ void main() { expect(valueLog, equals([ 0.0 ])); expect(controller.value, equals(0.0)); }); + + test('Can fling to upper and lower bounds', () { + WidgetsFlutterBinding.ensureInitialized(); + AnimationController controller = new AnimationController( + duration: const Duration(milliseconds: 100) + ); + + controller.fling(); + WidgetsBinding.instance.handleBeginFrame(const Duration(seconds: 1)); + WidgetsBinding.instance.handleBeginFrame(const Duration(seconds: 2)); + expect(controller.value, 1.0); + controller.stop(); + + AnimationController largeRangeController = new AnimationController( + duration: const Duration(milliseconds: 100), + lowerBound: -30.0, + upperBound: 45.0 + ); + + largeRangeController.fling(); + WidgetsBinding.instance.handleBeginFrame(const Duration(seconds: 3)); + WidgetsBinding.instance.handleBeginFrame(const Duration(seconds: 4)); + expect(largeRangeController.value, 45.0); + largeRangeController.fling(velocity: -1.0); + WidgetsBinding.instance.handleBeginFrame(const Duration(seconds: 5)); + WidgetsBinding.instance.handleBeginFrame(const Duration(seconds: 6)); + expect(largeRangeController.value, -30.0); + largeRangeController.stop(); + }); }