From 0f00f424fa1f2044bcf5eadbf87dba01bda8385d Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Mon, 16 Sep 2019 08:02:04 -0700 Subject: [PATCH] ModalRoutes ignore input when a (cupertino) pop transition is underway (#40466) --- packages/flutter/lib/src/widgets/routes.dart | 3 +- .../flutter/test/cupertino/route_test.dart | 76 ++++++++++++++++- packages/flutter/test/material/page_test.dart | 84 ++++++++++++++++++- 3 files changed, 160 insertions(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index d2fbaa71685..11419ba9531 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -654,7 +654,8 @@ class _ModalScopeState extends State<_ModalScope> { widget.route.animation, widget.route.secondaryAnimation, IgnorePointer( - ignoring: widget.route.animation?.status == AnimationStatus.reverse, + ignoring: widget.route.navigator.userGestureInProgress + || widget.route.animation?.status == AnimationStatus.reverse, child: child, ), ); diff --git a/packages/flutter/test/cupertino/route_test.dart b/packages/flutter/test/cupertino/route_test.dart index 2ac74a46ac9..0fe3a41a2cd 100644 --- a/packages/flutter/test/cupertino/route_test.dart +++ b/packages/flutter/test/cupertino/route_test.dart @@ -347,7 +347,18 @@ void main() { tester.getTopLeft(find.ancestor(of: find.text('route'), matching: find.byType(CupertinoPageScaffold))).dx, moreOrLessEquals(798, epsilon: 1), ); - await tester.tap(find.text('push')); + + // Use the navigator to push a route instead of tapping the 'push' button. + // The topmost route (the one that's animating away), ignores input while + // the pop is underway because route.navigator.userGestureInProgress. + Navigator.push(scaffoldKey.currentContext, CupertinoPageRoute( + builder: (BuildContext context) { + return const CupertinoPageScaffold( + child: Center(child: Text('route')), + ); + }, + )); + await tester.pumpAndSettle(); expect(find.text('route'), findsOneWidget); expect(find.text('push'), findsNothing); @@ -816,6 +827,69 @@ void main() { 0x7A000000, ); }); + + testWidgets('During back swipe the route ignores input', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/39989 + + final GlobalKey homeScaffoldKey = GlobalKey(); + final GlobalKey pageScaffoldKey = GlobalKey(); + int homeTapCount = 0; + int pageTapCount = 0; + + await tester.pumpWidget( + CupertinoApp( + home: CupertinoPageScaffold( + key: homeScaffoldKey, + child: GestureDetector( + onTap: () { + homeTapCount += 1; + } + ), + ), + ), + ); + + await tester.tap(find.byKey(homeScaffoldKey)); + expect(homeTapCount, 1); + expect(pageTapCount, 0); + + Navigator.push(homeScaffoldKey.currentContext, CupertinoPageRoute( + builder: (BuildContext context) { + return CupertinoPageScaffold( + key: pageScaffoldKey, + child: Padding( + padding: const EdgeInsets.all(16), + child: GestureDetector( + onTap: () { + pageTapCount += 1; + } + ), + ), + ); + }, + )); + + await tester.pumpAndSettle(); + await tester.tap(find.byKey(pageScaffoldKey)); + expect(homeTapCount, 1); + expect(pageTapCount, 1); + + // Start the basic iOS back-swipe dismiss transition. Drag the pushed + // "page" route halfway across the screen. The underlying "home" will + // start sliding in from the left. + + final TestGesture gesture = await tester.startGesture(const Offset(5, 300)); + await gesture.moveBy(const Offset(400, 0)); + await tester.pump(); + expect(tester.getTopLeft(find.byKey(pageScaffoldKey)), const Offset(400, 0)); + expect(tester.getTopLeft(find.byKey(homeScaffoldKey)).dx, lessThan(0)); + + // Tapping on the "page" route doesn't trigger the GestureDetector because + // it's being dragged. + await tester.tap(find.byKey(pageScaffoldKey)); + expect(homeTapCount, 1); + expect(pageTapCount, 1); + }); } class MockNavigatorObserver extends Mock implements NavigatorObserver {} diff --git a/packages/flutter/test/material/page_test.dart b/packages/flutter/test/material/page_test.dart index ee9f32d977c..aa10513f096 100644 --- a/packages/flutter/test/material/page_test.dart +++ b/packages/flutter/test/material/page_test.dart @@ -620,9 +620,91 @@ void main() { tester.getTopLeft(find.ancestor(of: find.text('route'), matching: find.byType(Scaffold))).dx, moreOrLessEquals(798, epsilon: 1), ); - await tester.tap(find.text('push')); + + // Use the navigator to push a route instead of tapping the 'push' button. + // The topmost route (the one that's animating away), ignores input while + // the pop is underway because route.navigator.userGestureInProgress. + Navigator.push(scaffoldKey.currentContext, MaterialPageRoute( + builder: (BuildContext context) { + return const Scaffold( + body: Center(child: Text('route')), + ); + }, + )); + await tester.pumpAndSettle(); expect(find.text('route'), findsOneWidget); expect(find.text('push'), findsNothing); }); + + testWidgets('During back swipe the route ignores input', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/39989 + + final GlobalKey homeScaffoldKey = GlobalKey(); + final GlobalKey pageScaffoldKey = GlobalKey(); + int homeTapCount = 0; + int pageTapCount = 0; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(platform: TargetPlatform.iOS), + home: Scaffold( + key: homeScaffoldKey, + body: GestureDetector( + onTap: () { + homeTapCount += 1; + } + ), + ), + ), + ); + + await tester.tap(find.byKey(homeScaffoldKey)); + expect(homeTapCount, 1); + expect(pageTapCount, 0); + + Navigator.push(homeScaffoldKey.currentContext, MaterialPageRoute( + builder: (BuildContext context) { + return Scaffold( + key: pageScaffoldKey, + appBar: AppBar(title: const Text('Page')), + body: Padding( + padding: const EdgeInsets.all(16), + child: GestureDetector( + onTap: () { + pageTapCount += 1; + } + ), + ), + ); + }, + )); + + await tester.pumpAndSettle(); + await tester.tap(find.byKey(pageScaffoldKey)); + expect(homeTapCount, 1); + expect(pageTapCount, 1); + + // Start the basic iOS back-swipe dismiss transition. Drag the pushed + // "page" route halfway across the screen. The underlying "home" will + // start sliding in from the left. + + final TestGesture gesture = await tester.startGesture(const Offset(5, 300)); + await gesture.moveBy(const Offset(400, 0)); + await tester.pump(); + expect(tester.getTopLeft(find.byKey(pageScaffoldKey)), const Offset(400, 0)); + expect(tester.getTopLeft(find.byKey(homeScaffoldKey)).dx, lessThan(0)); + + // Tapping on the "page" route doesn't trigger the GestureDetector because + // it's being dragged. + await tester.tap(find.byKey(pageScaffoldKey)); + expect(homeTapCount, 1); + expect(pageTapCount, 1); + + // Tapping the "page" route's back button doesn't do anything either. + await tester.tap(find.byTooltip('Back')); + await tester.pumpAndSettle(); + expect(tester.getTopLeft(find.byKey(pageScaffoldKey)), const Offset(400, 0)); + expect(tester.getTopLeft(find.byKey(homeScaffoldKey)).dx, lessThan(0)); + }); }