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