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;
+    }
+  });
 }