Simplify Scrollable animations

Rather than having two objects driving scrolling animations, we now have one
object, a Timeline, drive both scrollTo and fling animations. Using Timeline
instead of AnimatedSimulation paves the way to removing AnimatedSimulation
(which is now used only inside the animation library).

Finally, this patch also simplifies (and makes private) _TweenSimulation by
using AnimatedValue to do the math.
diff --git a/packages/flutter/lib/src/animation/animation_performance.dart b/packages/flutter/lib/src/animation/animation_performance.dart
index 9105498..8111754 100644
--- a/packages/flutter/lib/src/animation/animation_performance.dart
+++ b/packages/flutter/lib/src/animation/animation_performance.dart
@@ -85,7 +85,7 @@
   /// The progress of this performance along the timeline
   ///
   /// Note: Setting this value stops the current animation.
-  double get progress => _timeline.value;
+  double get progress => _timeline.value.clamp(0.0, 1.0);
   void set progress(double t) {
     // TODO(mpcomplete): should this affect |direction|?
     stop();
diff --git a/packages/flutter/lib/src/animation/timeline.dart b/packages/flutter/lib/src/animation/timeline.dart
index 75da47d..29005c7 100644
--- a/packages/flutter/lib/src/animation/timeline.dart
+++ b/packages/flutter/lib/src/animation/timeline.dart
@@ -5,30 +5,28 @@
 import 'dart:async';
 
 import 'package:newton/newton.dart';
-
+import 'package:sky/src/animation/curves.dart';
+import 'package:sky/src/animation/animated_value.dart';
 import 'package:sky/src/animation/animated_simulation.dart';
 
 /// A simulation that linearly varies from [begin] to [end] over [duration]
-class TweenSimulation extends Simulation {
+class _TweenSimulation extends Simulation {
   final double _durationInSeconds;
+  final AnimatedValue<double> _tween;
 
-  /// The initial value of the simulation
-  final double begin;
-
-  /// The terminal value of the simulation
-  final double end;
-
-  TweenSimulation(Duration duration, this.begin, this.end) :
-      _durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND {
+  _TweenSimulation(double begin, double end, Duration duration, Curve curve)
+    : _durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND,
+      _tween = new AnimatedValue<double>(begin, end: end, curve: curve) {
     assert(_durationInSeconds > 0.0);
-    assert(begin != null && begin >= 0.0 && begin <= 1.0);
-    assert(end != null && end >= 0.0 && end <= 1.0);
+    assert(begin != null);
+    assert(end != null);
   }
 
   double x(double timeInSeconds) {
     assert(timeInSeconds >= 0.0);
-    final double t = timeInSeconds / _durationInSeconds;
-    return t >= 1.0 ? end : begin + (end - begin) * t;
+    final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
+    _tween.setProgress(t, Direction.forward);
+    return _tween.value;
   }
 
   double dx(double timeInSeconds) => 1.0;
@@ -45,9 +43,9 @@
   AnimatedSimulation _animation;
 
   /// The current value of the timeline
-  double get value => _animation.value.clamp(0.0, 1.0);
+  double get value => _animation.value;
   void set value(double newValue) {
-    assert(newValue != null && newValue >= 0.0 && newValue <= 1.0);
+    assert(newValue != null);
     assert(!isAnimating);
     _animation.value = newValue;
   }
@@ -55,23 +53,14 @@
   /// Whether the timeline is currently animating
   bool get isAnimating => _animation.isAnimating;
 
-  Future _start({
-    Duration duration,
-    double begin: 0.0,
-    double end: 1.0
-  }) {
-    assert(!_animation.isAnimating);
-    assert(duration > Duration.ZERO);
-    return _animation.start(new TweenSimulation(duration, begin, end));
-  }
-
   /// Animate value of the timeline to the given target over the given duration
   ///
   /// Returns a future that resolves when the timeline stops animating,
   /// typically when the timeline arives at the target value.
-  Future animateTo(double target, { Duration duration }) {
+  Future animateTo(double target, { Duration duration, Curve curve: linear }) {
     assert(duration > Duration.ZERO);
-    return _start(duration: duration, begin: value, end: target);
+    assert(!_animation.isAnimating);
+    return _animation.start(new _TweenSimulation(value, target, duration, curve));
   }
 
   /// Stop animating the timeline
@@ -79,7 +68,7 @@
     _animation.stop();
   }
 
-  // Gives the given simulation control over the timeline
+  /// Gives the given simulation control over the timeline
   Future fling(Simulation simulation) {
     stop();
     return _animation.start(simulation);
diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart
index f5ac199..e09fe4f 100644
--- a/packages/flutter/lib/src/widgets/scrollable.dart
+++ b/packages/flutter/lib/src/widgets/scrollable.dart
@@ -51,16 +51,10 @@
     super.initState();
     if (config.initialScrollOffset is double)
       _scrollOffset = config.initialScrollOffset;
-    _toEndAnimation = new AnimatedSimulation(_setScrollOffset);
-    _toOffsetAnimation = new ValueAnimation<double>()
-      ..addListener(() {
-        AnimatedValue<double> offset = _toOffsetAnimation.variable;
-        _setScrollOffset(offset.value);
-      });
+    _animation = new Timeline(_setScrollOffset);
   }
 
-  AnimatedSimulation _toEndAnimation; // See _startToEndAnimation()
-  ValueAnimation<double> _toOffsetAnimation; // Started by scrollTo()
+  Timeline _animation;
 
   double _scrollOffset = 0.0;
   double get scrollOffset => _scrollOffset;
@@ -106,23 +100,10 @@
 
   Widget buildContent(BuildContext context);
 
-  Future _startToOffsetAnimation(double newScrollOffset, Duration duration, Curve curve) {
-    _stopAnimations();
-    _toOffsetAnimation
-      ..variable = new AnimatedValue<double>(scrollOffset,
-        end: newScrollOffset,
-        curve: curve
-      )
-      ..progress = 0.0
-      ..duration = duration;
-    return _toOffsetAnimation.play();
-  }
-
-  void _stopAnimations() {
-    if (_toOffsetAnimation.isAnimating)
-      _toOffsetAnimation.stop();
-    if (_toEndAnimation.isAnimating)
-      _toEndAnimation.stop();
+  Future _animateTo(double newScrollOffset, Duration duration, Curve curve) {
+    _animation.stop();
+    _animation.value = scrollOffset;
+    return _animation.animateTo(newScrollOffset, duration: duration, curve: curve);
   }
 
   bool _scrollOffsetIsInBounds(double offset) {
@@ -165,16 +146,16 @@
   }
 
   Future _startToEndAnimation({ double velocity }) {
-    _stopAnimations();
+    _animation.stop();
     Simulation simulation =
       _createSnapSimulation(velocity) ?? _createFlingSimulation(velocity ?? 0.0);
     if (simulation == null)
       return new Future.value();
-    return _toEndAnimation.start(simulation);
+    return _animation.fling(simulation);
   }
 
   void dispose() {
-    _stopAnimations();
+    _animation.stop();
     super.dispose();
   }
 
@@ -193,12 +174,12 @@
       return new Future.value();
 
     if (duration == null) {
-      _stopAnimations();
+      _animation.stop();
       _setScrollOffset(newScrollOffset);
       return new Future.value();
     }
 
-    return _startToOffsetAnimation(newScrollOffset, duration, curve);
+    return _animateTo(newScrollOffset, duration, curve);
   }
 
   Future scrollBy(double scrollDelta, { Duration duration, Curve curve }) {
@@ -209,7 +190,7 @@
   Future fling(Offset velocity) {
     if (velocity != Offset.zero)
       return _startToEndAnimation(velocity: _scrollVelocity(velocity));
-    if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating))
+    if (!_animation.isAnimating)
       return settleScrollOffset();
     return new Future.value();
   }
@@ -226,7 +207,7 @@
   }
 
   void _handlePointerDown(_) {
-    _stopAnimations();
+    _animation.stop();
   }
 
   void _handleDragUpdate(double delta) {