Merge pull request #1123 from mpcomplete/build.fix

Fix the build
diff --git a/sky/packages/sky/lib/gestures/fling.dart b/sky/packages/sky/lib/gestures/fling.dart
deleted file mode 100644
index 618e40b..0000000
--- a/sky/packages/sky/lib/gestures/fling.dart
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:sky' as sky;
-
-import 'package:sky/gestures/arena.dart';
-import 'package:sky/gestures/recognizer.dart';
-import 'package:sky/gestures/constants.dart';
-
-// Fling velocities are logical pixels per second.
-typedef void GestureFlingCallback(sky.Offset velocity);
-
-int _eventTime(sky.PointerEvent event) => (event.timeStamp * 1000.0).toInt(); // microseconds
-
-bool _isFlingGesture(sky.GestureVelocity velocity) {
-  double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y;
-  return velocity.isValid &&
-    velocitySquared > kMinFlingVelocity * kMinFlingVelocity &&
-    velocitySquared < kMaxFlingVelocity * kMaxFlingVelocity;
-}
-
-class FlingGestureRecognizer extends GestureRecognizer {
-  FlingGestureRecognizer({ PointerRouter router, this.onFling })
-    : super(router: router);
-
-  GestureFlingCallback onFling;
-  sky.VelocityTracker _velocityTracker = new sky.VelocityTracker();
-  int _primaryPointer;
-
-  void addPointer(sky.PointerEvent event) {
-    startTrackingPointer(event.pointer);
-    if (_primaryPointer == null)
-      _primaryPointer = event.pointer;
-  }
-
-  void handleEvent(sky.PointerEvent event) {
-    if (event.pointer == _primaryPointer) {
-      if (event.type == 'pointermove') {
-        _velocityTracker.addPosition(_eventTime(event), _primaryPointer, event.x, event.y);
-      } else if (event.type == 'pointerup') {
-        sky.GestureVelocity velocity = _velocityTracker.getVelocity(_primaryPointer);
-        if (_isFlingGesture(velocity)) {
-          resolve(GestureDisposition.accepted);
-          if (onFling != null)
-            onFling(new sky.Offset(velocity.x, velocity.y));
-        }
-      }
-    }
-    stopTrackingIfPointerNoLongerDown(event);
-  }
-
-  void didStopTrackingLastPointer() {
-    _primaryPointer = null;
-    _velocityTracker.reset();
-  }
-
-  void dispose() {
-    super.dispose();
-    _velocityTracker.reset();
-    _primaryPointer = null;
-  }
-}
diff --git a/sky/packages/sky/lib/gestures/recognizer.dart b/sky/packages/sky/lib/gestures/recognizer.dart
index 6834552..9fd068d 100644
--- a/sky/packages/sky/lib/gestures/recognizer.dart
+++ b/sky/packages/sky/lib/gestures/recognizer.dart
@@ -25,7 +25,7 @@
   void handleEvent(sky.PointerEvent event);
   void acceptGesture(int pointer) { }
   void rejectGesture(int pointer) { }
-  void didStopTrackingLastPointer();
+  void didStopTrackingLastPointer(int pointer);
 
   void resolve(GestureDisposition disposition) {
     List<GestureArenaEntry> localEntries = new List.from(_entries);
@@ -53,7 +53,7 @@
     _router.removeRoute(pointer, handleEvent);
     _trackedPointers.remove(pointer);
     if (_trackedPointers.isEmpty)
-      didStopTrackingLastPointer();
+      didStopTrackingLastPointer(pointer);
   }
 
   void stopTrackingIfPointerNoLongerDown(sky.PointerEvent event) {
@@ -124,7 +124,7 @@
     }
   }
 
-  void didStopTrackingLastPointer() {
+  void didStopTrackingLastPointer(int pointer) {
     _stopTimer();
     state = GestureRecognizerState.ready;
   }
diff --git a/sky/packages/sky/lib/gestures/scroll.dart b/sky/packages/sky/lib/gestures/scroll.dart
index 26d11c3..7f65132 100644
--- a/sky/packages/sky/lib/gestures/scroll.dart
+++ b/sky/packages/sky/lib/gestures/scroll.dart
@@ -16,14 +16,26 @@
 
 typedef void GestureDragStartCallback();
 typedef void GestureDragUpdateCallback(double scrollDelta);
-typedef void GestureDragEndCallback();
+typedef void GestureDragEndCallback(sky.Offset velocity);
 
 typedef void GesturePanStartCallback();
 typedef void GesturePanUpdateCallback(sky.Offset scrollDelta);
-typedef void GesturePanEndCallback();
+typedef void GesturePanEndCallback(sky.Offset velocity);
 
 typedef void _GesturePolymorphicUpdateCallback<T>(T scrollDelta);
 
+// Fling velocities are logical pixels per second.
+typedef void GestureFlingCallback(sky.Offset velocity);
+
+int _eventTime(sky.PointerEvent event) => (event.timeStamp * 1000.0).toInt(); // microseconds
+
+bool _isFlingGesture(sky.GestureVelocity velocity) {
+  double velocitySquared = velocity.x * velocity.x + velocity.y * velocity.y;
+  return velocity.isValid &&
+    velocitySquared > kMinFlingVelocity * kMinFlingVelocity &&
+    velocitySquared < kMaxFlingVelocity * kMaxFlingVelocity;
+}
+
 abstract class _DragGestureRecognizer<T extends dynamic> extends GestureRecognizer {
   _DragGestureRecognizer({ PointerRouter router, this.onStart, this.onUpdate, this.onEnd })
     : super(router: router);
@@ -39,6 +51,8 @@
   T _getDragDelta(sky.PointerEvent event);
   bool get _hasSufficientPendingDragDeltaToAccept;
 
+  final sky.VelocityTracker _velocityTracker = new sky.VelocityTracker();
+
   void addPointer(sky.PointerEvent event) {
     startTrackingPointer(event.pointer);
     if (_state == DragState.ready) {
@@ -50,6 +64,7 @@
   void handleEvent(sky.PointerEvent event) {
     assert(_state != DragState.ready);
     if (event.type == 'pointermove') {
+      _velocityTracker.addPosition(_eventTime(event), event.pointer, event.x, event.y);
       T delta = _getDragDelta(event);
       if (_state == DragState.accepted) {
         if (onUpdate != null)
@@ -75,7 +90,7 @@
     }
   }
 
-  void didStopTrackingLastPointer() {
+  void didStopTrackingLastPointer(int pointer) {
     if (_state == DragState.possible) {
       resolve(GestureDisposition.rejected);
       _state = DragState.ready;
@@ -83,8 +98,20 @@
     }
     bool wasAccepted = (_state == DragState.accepted);
     _state = DragState.ready;
-    if (wasAccepted && onEnd != null)
-      onEnd();
+    if (wasAccepted && onEnd != null) {
+      sky.GestureVelocity gestureVelocity = _velocityTracker.getVelocity(pointer);
+      sky.Offset velocity = sky.Offset.zero;
+      if (_isFlingGesture(gestureVelocity))
+        velocity = new sky.Offset(gestureVelocity.x, gestureVelocity.y);
+      resolve(GestureDisposition.accepted);
+      onEnd(velocity);
+    }
+    _velocityTracker.reset();
+  }
+
+  void dispose() {
+    _velocityTracker.reset();
+    super.dispose();
   }
 }
 
diff --git a/sky/packages/sky/lib/src/animation/scroll_behavior.dart b/sky/packages/sky/lib/src/animation/scroll_behavior.dart
index cd20976..14d47b0 100644
--- a/sky/packages/sky/lib/src/animation/scroll_behavior.dart
+++ b/sky/packages/sky/lib/src/animation/scroll_behavior.dart
@@ -19,6 +19,9 @@
 
   /// The new scroll offset to use when the user attempts to scroll from the given offset by the given delta
   double applyCurve(double scrollOffset, double scrollDelta);
+
+  /// Whether this scroll behavior currently permits scrolling
+  bool get isScrollable => true;
 }
 
 /// A scroll behavior for a scrollable widget with linear extent
diff --git a/sky/packages/sky/lib/src/widgets/dismissable.dart b/sky/packages/sky/lib/src/widgets/dismissable.dart
index 7425cdb..e895ef2 100644
--- a/sky/packages/sky/lib/src/widgets/dismissable.dart
+++ b/sky/packages/sky/lib/src/widgets/dismissable.dart
@@ -155,7 +155,7 @@
       _fadePerformance.progress = _dragExtent.abs() / (_size.width * _kDismissCardThreshold);
   }
 
-  _handleDragEnd() {
+  void _handleDragEnd(Offset velocity) {
     if (!_isActive || _fadePerformance.isAnimating)
       return;
     _dragUnderway = false;
diff --git a/sky/packages/sky/lib/src/widgets/gesture_detector.dart b/sky/packages/sky/lib/src/widgets/gesture_detector.dart
index ddc2295..15436c9 100644
--- a/sky/packages/sky/lib/src/widgets/gesture_detector.dart
+++ b/sky/packages/sky/lib/src/widgets/gesture_detector.dart
@@ -4,7 +4,6 @@
 
 import 'dart:sky' as sky;
 
-import 'package:sky/gestures/fling.dart';
 import 'package:sky/gestures/long_press.dart';
 import 'package:sky/gestures/recognizer.dart';
 import 'package:sky/gestures/scroll.dart';
@@ -28,8 +27,7 @@
     this.onHorizontalDragEnd,
     this.onPanStart,
     this.onPanUpdate,
-    this.onPanEnd,
-    this.onFling
+    this.onPanEnd
   }) : super(key: key);
 
   Widget child;
@@ -49,8 +47,6 @@
   GesturePanUpdateCallback onPanUpdate;
   GesturePanEndCallback onPanEnd;
 
-  GestureFlingCallback onFling;
-
   void syncConstructorArguments(GestureDetector source) {
     child = source.child;
     onTap = source.onTap;
@@ -65,7 +61,6 @@
     onPanStart = source.onPanStart;
     onPanUpdate = source.onPanUpdate;
     onPanEnd = source.onPanEnd;
-    onFling = source.onFling;
     _syncGestureListeners();
   }
 
@@ -113,13 +108,6 @@
     return _pan;
   }
 
-  FlingGestureRecognizer _fling;
-  FlingGestureRecognizer _ensureFling() {
-    if (_fling == null)
-      _fling = new FlingGestureRecognizer(router: _router);
-    return _fling;
-  }
-
   void didMount() {
     super.didMount();
     _syncGestureListeners();
@@ -133,7 +121,6 @@
     _verticalDrag = _ensureDisposed(_verticalDrag);
     _horizontalDrag = _ensureDisposed(_horizontalDrag);
     _pan = _ensureDisposed(_pan);
-    _fling = _ensureDisposed(_fling);
   }
 
   void _syncGestureListeners() {
@@ -143,7 +130,6 @@
     _syncVerticalDrag();
     _syncHorizontalDrag();
     _syncPan();
-    _syncFling();
   }
 
   void _syncTap() {
@@ -200,13 +186,6 @@
     }
   }
 
-  void _syncFling() {
-    if (onFling == null)
-      _fling = _ensureDisposed(_fling);
-    else
-      _ensureFling().onFling = onFling;
-  }
-
   GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) {
     recognizer?.dispose();
     return null;
@@ -225,8 +204,6 @@
       _horizontalDrag.addPointer(event);
     if (_pan != null)
       _pan.addPointer(event);
-    if (_fling != null)
-      _fling.addPointer(event);
     return EventDisposition.processed;
   }
 
diff --git a/sky/packages/sky/lib/src/widgets/scrollable.dart b/sky/packages/sky/lib/src/widgets/scrollable.dart
index 2fb8efa..217a4d4 100644
--- a/sky/packages/sky/lib/src/widgets/scrollable.dart
+++ b/sky/packages/sky/lib/src/widgets/scrollable.dart
@@ -9,6 +9,7 @@
 import 'package:newton/newton.dart';
 import 'package:sky/animation.dart';
 import 'package:sky/gestures/constants.dart';
+import 'package:sky/gestures/scroll.dart';
 import 'package:sky/src/rendering/box.dart';
 import 'package:sky/src/rendering/viewport.dart';
 import 'package:sky/src/widgets/basic.dart';
@@ -79,17 +80,27 @@
 
   Widget buildContent();
 
+  GestureDragUpdateCallback _getDragUpdateHandler(ScrollDirection direction) {
+    if (scrollDirection != direction || !scrollBehavior.isScrollable)
+      return null;
+    return scrollBy;
+  }
+
+  GestureDragEndCallback _getDragEndHandler(ScrollDirection direction) {
+    if (scrollDirection != direction || !scrollBehavior.isScrollable)
+      return null;
+    return _handleDragEnd;
+  }
+
   Widget build() {
     return new GestureDetector(
-      onVerticalDragUpdate: scrollDirection == ScrollDirection.vertical ? scrollBy : null,
-      onVerticalDragEnd: scrollDirection == ScrollDirection.vertical ? _maybeSettleScrollOffset : null,
-      onHorizontalDragUpdate: scrollDirection == ScrollDirection.horizontal ? scrollBy : null,
-      onHorizontalDragEnd: scrollDirection == ScrollDirection.horizontal ? _maybeSettleScrollOffset : null,
-      onFling: _handleFling,
+      onVerticalDragUpdate: _getDragUpdateHandler(ScrollDirection.vertical),
+      onVerticalDragEnd: _getDragEndHandler(ScrollDirection.vertical),
+      onHorizontalDragUpdate: _getDragUpdateHandler(ScrollDirection.horizontal),
+      onHorizontalDragEnd: _getDragEndHandler(ScrollDirection.horizontal),
       child: new Listener(
         child: buildContent(),
         onPointerDown: _handlePointerDown,
-        onGestureFlingCancel: _handleFlingCancel,
         onWheel: _handleWheel
       )
     );
@@ -170,19 +181,12 @@
     return EventDisposition.processed;
   }
 
-  void _handleFling(Offset velocity) {
-    _startToEndAnimation(velocity: _scrollVelocity(velocity));
-  }
-
-  void _maybeSettleScrollOffset() {
-    if (!_toEndAnimation.isAnimating &&
-        (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating))
+  void _handleDragEnd(Offset velocity) {
+    if (velocity != Offset.zero) {
+      _startToEndAnimation(velocity: _scrollVelocity(velocity));
+    } else if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating)) {
       settleScrollOffset();
-  }
-
-  EventDisposition _handleFlingCancel(sky.GestureEvent event) {
-    _maybeSettleScrollOffset();
-    return EventDisposition.processed;
+    }
   }
 
   EventDisposition _handleWheel(sky.WheelEvent event) {
@@ -557,7 +561,7 @@
       .clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset);
   }
 
-  void _handleFling(sky.Offset velocity) {
+  void _handleDragEnd(sky.Offset velocity) {
     double scrollVelocity = _scrollVelocity(velocity);
     double newScrollOffset = _snapScrollOffset(scrollOffset + scrollVelocity.sign * itemExtent)
       .clamp(_snapScrollOffset(scrollOffset - itemExtent / 2.0),
diff --git a/sky/unit/test/gestures/scroll_test.dart b/sky/unit/test/gestures/scroll_test.dart
index 793dabb..60c8538 100644
--- a/sky/unit/test/gestures/scroll_test.dart
+++ b/sky/unit/test/gestures/scroll_test.dart
@@ -25,7 +25,7 @@
     };
 
     bool didEndPan = false;
-    pan.onEnd = () {
+    pan.onEnd = (sky.Offset velocity) {
       didEndPan = true;
     };
 
diff --git a/sky/unit/test/widget/gesture_detector_test.dart b/sky/unit/test/widget/gesture_detector_test.dart
index b07f374..2902914 100644
--- a/sky/unit/test/widget/gesture_detector_test.dart
+++ b/sky/unit/test/widget/gesture_detector_test.dart
@@ -21,7 +21,7 @@
         onVerticalDragUpdate: (double scrollDelta) {
           updatedDragDelta = scrollDelta;
         },
-        onVerticalDragEnd: () {
+        onVerticalDragEnd: (Offset velocity) {
           didEndDrag = true;
         },
         child: new Container()
@@ -69,9 +69,9 @@
     Widget builder() {
       return new GestureDetector(
         onVerticalDragUpdate: (double delta) { dragDistance += delta; },
-        onVerticalDragEnd: () { gestureCount += 1; },
+        onVerticalDragEnd: (Offset velocity) { gestureCount += 1; },
         onHorizontalDragUpdate: (_) { fail("gesture should not match"); },
-        onHorizontalDragEnd: () { fail("gesture should not match"); },
+        onHorizontalDragEnd: (Offset velocity) { fail("gesture should not match"); },
         child: new Container()
       );
     }