Merge pull request #1549 from HansMuller/dismissable_dispose

Defunct DismissableState should not have live animations
diff --git a/packages/flutter/lib/src/widgets/drawer.dart b/packages/flutter/lib/src/widgets/drawer.dart
index e294eee..2b63b2a 100644
--- a/packages/flutter/lib/src/widgets/drawer.dart
+++ b/packages/flutter/lib/src/widgets/drawer.dart
@@ -35,53 +35,72 @@
 const Point _kClosedPosition = const Point(-_kWidth, 0.0);
 
 class _Drawer extends StatelessComponent {
+
   _Drawer({
     Key key,
     this.child,
     this.level: 3,
     this.performance,
+    this.interactive,
     this.route
   }) : super(key: key);
 
   final Widget child;
   final int level;
   final PerformanceView performance;
+  final bool interactive;
   final _DrawerRoute route;
 
   Widget build(BuildContext context) {
-    Widget mask = new GestureDetector(
-      onTap: route.close,
-      child: new ColorTransition(
-        performance: performance,
-        color: new AnimatedColorValue(Colors.transparent, end: Colors.black54),
-        child: new Container()
-      )
+    return new GestureDetector(
+      onHorizontalDragStart: () {
+        if (interactive)
+          route._takeControl();
+      },
+      onHorizontalDragUpdate: (double delta) {
+        if (interactive)
+          route._moveDrawer(delta);
+      },
+      onHorizontalDragEnd: (Offset velocity) {
+        if (interactive)
+          route._settle(velocity);
+      },
+      child: new Stack([
+        // mask
+        new GestureDetector(
+          onTap: () {
+            if (interactive)
+              route._close();
+          },
+          child: new ColorTransition(
+            performance: performance,
+            color: new AnimatedColorValue(Colors.transparent, end: Colors.black54),
+            child: new Container()
+          )
+        ),
+        // drawer
+        new Positioned(
+          top: 0.0,
+          left: 0.0,
+          bottom: 0.0,
+          child: new SlideTransition(
+            performance: performance,
+            position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
+            child: new AnimatedContainer(
+              curve: ease,
+              duration: _kThemeChangeDuration,
+              decoration: new BoxDecoration(
+                backgroundColor: Theme.of(context).canvasColor,
+                boxShadow: shadows[level]),
+              width: _kWidth,
+              child: child
+            )
+          )
+        )
+      ])
     );
-
-    Widget content = new SlideTransition(
-      performance: performance,
-      position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
-      child: new AnimatedContainer(
-        curve: ease,
-        duration: _kThemeChangeDuration,
-        decoration: new BoxDecoration(
-          backgroundColor: Theme.of(context).canvasColor,
-          boxShadow: shadows[level]),
-        width: _kWidth,
-        child: child
-      )
-    );
-
-    return new Stack([
-      mask,
-      new Positioned(
-        top: 0.0,
-        left: 0.0,
-        bottom: 0.0,
-        child: content
-      )
-    ]);
   }
+
 }
 
 class _DrawerRoute extends Route {
@@ -95,23 +114,18 @@
 
   bool get opaque => false;
 
+  bool _interactive = true;
+
   Widget build(NavigatorState navigator, PerformanceView nextRoutePerformance) {
     return new Focus(
       key: new GlobalObjectKey(this),
       autofocus: true,
-      child: new GestureDetector(
-        onHorizontalDragStart: () {
-          _performance?.stop();
-        },
-        onHorizontalDragUpdate: (double delta) {
-          _performance?.progress = delta / _kWidth;
-        },
-        onHorizontalDragEnd: _settle,
-        child: new _Drawer(
-          child: child,
-          level: level,
-          performance: performance
-        )
+      child: new _Drawer(
+        child: child,
+        level: level,
+        performance: performance,
+        interactive: _interactive,
+        route: this
       )
     );
   }
@@ -122,26 +136,44 @@
   }
 
   void didPop([dynamic result]) {
+    assert(result == null); // because we don't do anything with it, so otherwise it'd be lost
     super.didPop(result);
-    _performance.reverse();
-    _performance = null;
+    if (_performance.status != PerformanceStatus.dismissed)
+      _performance.reverse();
+    setState(() {
+      _interactive = false;
+      // TODO(ianh): https://github.com/flutter/engine/issues/1539
+    });
+  }
+
+  void _takeControl() {
+    assert(_interactive);
+    _performance.stop();
+  }
+
+  void _moveDrawer(double delta) {
+    assert(_interactive);
+    _performance.progress += delta / _kWidth;
   }
 
   void _settle(Offset velocity) {
+    assert(_interactive);
     if (velocity.dx.abs() >= _kMinFlingVelocity) {
-      _performance?.fling(velocity: velocity.dx * _kFlingVelocityScale);
-    } else if (_performance?.progress < 0.5) {
-      close();
+      _performance.fling(velocity: velocity.dx * _kFlingVelocityScale);
+    } else if (_performance.progress < 0.5) {
+      _close();
     } else {
-      _performance?.fling(velocity: 1.0);
+      _performance.fling(velocity: 1.0);
     }
   }
 
-  void close() {
-    _performance?.fling(velocity: -1.0);
+  void _close() {
+    assert(_interactive);
+    _performance.fling(velocity: -1.0);
   }
 }
 
 void showDrawer({ NavigatorState navigator, Widget child, int level: 3 }) {
+  assert(navigator != null);
   navigator.push(new _DrawerRoute(child: child, level: level));
 }
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index 13b001b..04e839f 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -220,6 +220,11 @@
 
   NavigatorState _navigator;
 
+  void setState(void fn()) {
+    assert(_navigator != null);
+    _navigator.setState(fn);
+  }
+
   void didPush(NavigatorState navigator) {
     assert(_navigator == null);
     _navigator = navigator;
diff --git a/packages/unit/test/widget/drawer_test.dart b/packages/unit/test/widget/drawer_test.dart
new file mode 100644
index 0000000..2fb0b24
--- /dev/null
+++ b/packages/unit/test/widget/drawer_test.dart
@@ -0,0 +1,70 @@
+import 'package:sky/widgets.dart';
+import 'package:test/test.dart';
+
+import 'widget_tester.dart';
+
+void main() {
+
+  test('Drawer control test', () {
+    testWidgets((WidgetTester tester) {
+      NavigatorState navigator;
+      tester.pumpWidget(
+        new App(
+          routes: {
+            '/': (RouteArguments args) {
+              navigator = args.navigator;
+              new Container();
+            }
+          }
+        )
+      );
+      tester.pump(); // no effect
+      expect(tester.findText('drawer'), isNull);
+      showDrawer(navigator: navigator, child: new Text('drawer'));
+      tester.pump(); // drawer should be starting to animate in
+      expect(tester.findText('drawer'), isNotNull);
+      tester.pump(new Duration(seconds: 1)); // animation done
+      expect(tester.findText('drawer'), isNotNull);
+      navigator.pop();
+      tester.pump(); // drawer should be starting to animate away
+      expect(tester.findText('drawer'), isNotNull);
+      tester.pump(new Duration(seconds: 1)); // animation done
+      expect(tester.findText('drawer'), isNull);
+    });
+  });
+
+  test('Drawer tap test', () {
+    testWidgets((WidgetTester tester) {
+      NavigatorState navigator;
+      tester.pumpWidget(new Container()); // throw away the old App and its Navigator
+      tester.pumpWidget(
+        new App(
+          routes: {
+            '/': (RouteArguments args) {
+              navigator = args.navigator;
+              new Container();
+            }
+          }
+        )
+      );
+      tester.pump(); // no effect
+      expect(tester.findText('drawer'), isNull);
+      showDrawer(navigator: navigator, child: new Text('drawer'));
+      tester.pump(); // drawer should be starting to animate in
+      expect(tester.findText('drawer'), isNotNull);
+      tester.pump(new Duration(seconds: 1)); // animation done
+      expect(tester.findText('drawer'), isNotNull);
+      tester.tap(tester.findText('drawer'));
+      tester.pump(); // nothing should have happened
+      expect(tester.findText('drawer'), isNotNull);
+      tester.pump(new Duration(seconds: 1)); // ditto
+      expect(tester.findText('drawer'), isNotNull);
+      tester.tapAt(const Point(750.0, 100.0)); // on the mask
+      tester.pump(); // drawer should be starting to animate away
+      expect(tester.findText('drawer'), isNotNull);
+      tester.pump(new Duration(seconds: 1)); // animation done
+      expect(tester.findText('drawer'), isNull);
+    });
+  });
+
+}