Adjust ClampingScrollSimulation Parameters
diff --git a/packages/flutter/lib/src/widgets/scroll_simulation.dart b/packages/flutter/lib/src/widgets/scroll_simulation.dart
index 3e9a1c2..9aa414b 100644
--- a/packages/flutter/lib/src/widgets/scroll_simulation.dart
+++ b/packages/flutter/lib/src/widgets/scroll_simulation.dart
@@ -202,14 +202,28 @@
// Scale f(t) so that 0.0 <= f(t) <= 1.0
// f(t) = (1165.03 t^3 - 3143.62 t^2 + 2945.87 t) / 961.0
// = 1.2 t^3 - 3.27 t^2 + 3.065 t
- static const double _initialVelocityPenetration = 3.065;
+ //
+ // TODO(grouma): The following logic works around the poor fit of the
+ // cubic curve. Ideally we re-implement the scroll logic found on Android.
+ //
+ // The above cubic curve fit results in a velocity function of:
+ // v(t) = 3.6 t^2 - 6.54 t + 3.065
+ // Which has a global minimum of 379/4000 at time 109/120.
+ //
+ // To ensure velocity is 0 at time 1 we shift the velocity function:
+ // v(t) = 3.6 (1 - 2 t + t^2)
+ //
+ // Integrating the velocity function to preserve the feel results in the
+ // following fit function:
+ // f(t) = 1.2 t^3 - 3.6 t ^2 + 3.6
+ static const double _initialVelocityPenetration = 3.6;
static double _flingDistancePenetration(double t) {
- return (1.2 * t * t * t) - (3.27 * t * t) + (_initialVelocityPenetration * t);
+ return (1.2 * t * t * t) - (3.6 * t * t) + (_initialVelocityPenetration * t);
}
// The derivative of the _flingDistancePenetration() function.
static double _flingVelocityPenetration(double t) {
- return (3.6 * t * t) - (6.54 * t) + _initialVelocityPenetration;
+ return (3.6 * t * t) - (7.2 * t) + _initialVelocityPenetration;
}
@override
diff --git a/packages/flutter/test/widgets/scroll_simulation_test.dart b/packages/flutter/test/widgets/scroll_simulation_test.dart
index 69958f9..1639ffc 100644
--- a/packages/flutter/test/widgets/scroll_simulation_test.dart
+++ b/packages/flutter/test/widgets/scroll_simulation_test.dart
@@ -23,4 +23,34 @@
checkInitialConditions(75.0, 614.2093);
checkInitialConditions(5469.0, 182.114534);
});
+
+ test('ClampingScrollSimulation velocity eventually reaches zero', () {
+ void checkFinalConditions(double position, double velocity) {
+ final ClampingScrollSimulation simulation =
+ ClampingScrollSimulation(position: position, velocity: velocity);
+ expect(simulation.dx(10.0), equals(0.0));
+ }
+
+ checkFinalConditions(51.0, 2000.0);
+ checkFinalConditions(584.0, 2617.294734);
+ checkFinalConditions(345.0, 1982.785934);
+ checkFinalConditions(0.0, 1831.366634);
+ checkFinalConditions(-156.2, 1541.57665);
+ checkFinalConditions(4.0, 1139.250439);
+ checkFinalConditions(4534.0, 1073.553798);
+ checkFinalConditions(75.0, 614.2093);
+ checkFinalConditions(5469.0, 182.114534);
+ });
+
+ test('ClampingScrollSimulation velocity is monotonically decreasing', () {
+ final ClampingScrollSimulation simulation =
+ ClampingScrollSimulation(position: 50, velocity: 200);
+
+ double previous = simulation.dx(0);
+ for (double t = 0; t <= 1; t += 0.05) {
+ final double current = simulation.dx(t);
+ expect(current, lessThanOrEqualTo(previous));
+ previous = current;
+ }
+ });
}