Revert "Add ability for `ModalRoutes` to ignore pointers during transitions and do so on `Cupertino` routes (#95757)" (#104520) (#104532)
This reverts commit 4c0b0be2da5d1ee80c3d713e68ddd88d2cf2e72d.
diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart
index 0d268c2..1968f40 100644
--- a/packages/flutter/lib/src/cupertino/route.dart
+++ b/packages/flutter/lib/src/cupertino/route.dart
@@ -235,9 +235,6 @@
return result;
}
- @override
- bool get ignorePointerDuringTransitions => true;
-
// Called by _CupertinoBackGestureDetector when a pop ("back") drag start
// gesture is detected. The returned controller handles all of the subsequent
// drag events.
@@ -1052,9 +1049,6 @@
@override
Duration get transitionDuration => _kModalPopupTransitionDuration;
- @override
- bool get ignorePointerDuringTransitions => true;
-
Animation<double>? _animation;
late Tween<Offset> _offsetTween;
@@ -1355,7 +1349,4 @@
barrierLabel: barrierLabel ?? CupertinoLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: barrierColor ?? CupertinoDynamicColor.resolve(kCupertinoModalBarrierColor, context),
);
-
- @override
- bool get ignorePointerDuringTransitions => true;
}
diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart
index 4ebce88..83bcdce 100644
--- a/packages/flutter/lib/src/widgets/routes.dart
+++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -293,85 +293,73 @@
final VoidCallback? previousTrainHoppingListenerRemover = _trainHoppingListenerRemover;
_trainHoppingListenerRemover = null;
- if (nextRoute is TransitionRoute<dynamic>) {
- if (canTransitionTo(nextRoute) && nextRoute.canTransitionFrom(this)) {
- final Animation<double>? current = _secondaryAnimation.parent;
- if (current != null) {
- final Animation<double> currentTrain = (current is TrainHoppingAnimation ? current.currentTrain : current)!;
- final Animation<double> nextTrain = nextRoute._animation!;
- if (
- currentTrain.value == nextTrain.value ||
- nextTrain.status == AnimationStatus.completed ||
- nextTrain.status == AnimationStatus.dismissed
- ) {
- _setSecondaryAnimation(nextTrain, nextRoute.completed);
- } else {
- // Two trains animate at different values. We have to do train hopping.
- // There are three possibilities of train hopping:
- // 1. We hop on the nextTrain when two trains meet in the middle using
- // TrainHoppingAnimation.
- // 2. There is no chance to hop on nextTrain because two trains never
- // cross each other. We have to directly set the animation to
- // nextTrain once the nextTrain stops animating.
- // 3. A new _updateSecondaryAnimation is called before train hopping
- // finishes. We leave a listener remover for the next call to
- // properly clean up the existing train hopping.
- TrainHoppingAnimation? newAnimation;
- void jumpOnAnimationEnd(AnimationStatus status) {
- switch (status) {
- case AnimationStatus.completed:
- case AnimationStatus.dismissed:
- // The nextTrain has stopped animating without train hopping.
- // Directly sets the secondary animation and disposes the
- // TrainHoppingAnimation.
- _setSecondaryAnimation(nextTrain, nextRoute.completed);
- if (_trainHoppingListenerRemover != null) {
- _trainHoppingListenerRemover!();
- _trainHoppingListenerRemover = null;
- }
- break;
- case AnimationStatus.forward:
- case AnimationStatus.reverse:
- break;
- }
- }
- _trainHoppingListenerRemover = () {
- nextTrain.removeStatusListener(jumpOnAnimationEnd);
- newAnimation?.dispose();
- };
- nextTrain.addStatusListener(jumpOnAnimationEnd);
- newAnimation = TrainHoppingAnimation(
- currentTrain,
- nextTrain,
- onSwitchedTrain: () {
- assert(_secondaryAnimation.parent == newAnimation);
- assert(newAnimation!.currentTrain == nextRoute._animation);
- // We can hop on the nextTrain, so we don't need to listen to
- // whether the nextTrain has stopped.
- _setSecondaryAnimation(newAnimation!.currentTrain, nextRoute.completed);
+ if (nextRoute is TransitionRoute<dynamic> && canTransitionTo(nextRoute) && nextRoute.canTransitionFrom(this)) {
+ final Animation<double>? current = _secondaryAnimation.parent;
+ if (current != null) {
+ final Animation<double> currentTrain = (current is TrainHoppingAnimation ? current.currentTrain : current)!;
+ final Animation<double> nextTrain = nextRoute._animation!;
+ if (
+ currentTrain.value == nextTrain.value ||
+ nextTrain.status == AnimationStatus.completed ||
+ nextTrain.status == AnimationStatus.dismissed
+ ) {
+ _setSecondaryAnimation(nextTrain, nextRoute.completed);
+ } else {
+ // Two trains animate at different values. We have to do train hopping.
+ // There are three possibilities of train hopping:
+ // 1. We hop on the nextTrain when two trains meet in the middle using
+ // TrainHoppingAnimation.
+ // 2. There is no chance to hop on nextTrain because two trains never
+ // cross each other. We have to directly set the animation to
+ // nextTrain once the nextTrain stops animating.
+ // 3. A new _updateSecondaryAnimation is called before train hopping
+ // finishes. We leave a listener remover for the next call to
+ // properly clean up the existing train hopping.
+ TrainHoppingAnimation? newAnimation;
+ void jumpOnAnimationEnd(AnimationStatus status) {
+ switch (status) {
+ case AnimationStatus.completed:
+ case AnimationStatus.dismissed:
+ // The nextTrain has stopped animating without train hopping.
+ // Directly sets the secondary animation and disposes the
+ // TrainHoppingAnimation.
+ _setSecondaryAnimation(nextTrain, nextRoute.completed);
if (_trainHoppingListenerRemover != null) {
_trainHoppingListenerRemover!();
_trainHoppingListenerRemover = null;
}
- },
- );
- _setSecondaryAnimation(newAnimation, nextRoute.completed);
+ break;
+ case AnimationStatus.forward:
+ case AnimationStatus.reverse:
+ break;
+ }
}
- } else { // This route has no secondary animation.
- _setSecondaryAnimation(nextRoute._animation, nextRoute.completed);
+ _trainHoppingListenerRemover = () {
+ nextTrain.removeStatusListener(jumpOnAnimationEnd);
+ newAnimation?.dispose();
+ };
+ nextTrain.addStatusListener(jumpOnAnimationEnd);
+ newAnimation = TrainHoppingAnimation(
+ currentTrain,
+ nextTrain,
+ onSwitchedTrain: () {
+ assert(_secondaryAnimation.parent == newAnimation);
+ assert(newAnimation!.currentTrain == nextRoute._animation);
+ // We can hop on the nextTrain, so we don't need to listen to
+ // whether the nextTrain has stopped.
+ _setSecondaryAnimation(newAnimation!.currentTrain, nextRoute.completed);
+ if (_trainHoppingListenerRemover != null) {
+ _trainHoppingListenerRemover!();
+ _trainHoppingListenerRemover = null;
+ }
+ },
+ );
+ _setSecondaryAnimation(newAnimation, nextRoute.completed);
}
} else {
- // This route cannot coordinate transitions with nextRoute, so it should
- // have no visible secondary animation. By using an AnimationMin, the
- // animation's value will always be zero, but it will have nextRoute.animation's
- // status until it finishes, allowing this route to wait until all visible
- // transitions are complete to stop ignoring pointers.
- _setSecondaryAnimation(
- AnimationMin<double>(kAlwaysDismissedAnimation, nextRoute._animation!),
- nextRoute.completed,
- );
+ _setSecondaryAnimation(nextRoute._animation, nextRoute.completed);
}
- } else { // The next route is not a TransitionRoute.
+ } else {
_setSecondaryAnimation(kAlwaysDismissedAnimation);
}
// Finally, we dispose any previous train hopping animation because it
@@ -408,9 +396,9 @@
/// the [nextRoute] is popped off of this route, the
/// `secondaryAnimation` will run from 1.0 - 0.0.
///
- /// If false, this route's [ModalRoute.buildTransitions] `secondaryAnimation`
- /// will proxy an animation with a constant value of 0. In other words, this
- /// route will not animate when [nextRoute] is pushed on top of it or when
+ /// If false, this route's [ModalRoute.buildTransitions] `secondaryAnimation` parameter
+ /// value will be [kAlwaysDismissedAnimation]. In other words, this route
+ /// will not animate when [nextRoute] is pushed on top of it or when
/// [nextRoute] is popped off of it.
///
/// Returns true by default.
@@ -858,19 +846,17 @@
context,
widget.route.animation!,
widget.route.secondaryAnimation!,
- // _listenable updates when this route's animations change
- // values, but the _ignorePointerNotifier can also update
- // when the status of animations on popping routes change,
- // even when this route's animations' values don't. Also,
- // when the value of the _ignorePointerNotifier changes,
- // it's only necessary to rebuild the IgnorePointer
- // widget and set the focus node's ability to focus.
+ // This additional AnimatedBuilder is include because if the
+ // value of the userGestureInProgressNotifier changes, it's
+ // only necessary to rebuild the IgnorePointer widget and set
+ // the focus node's ability to focus.
AnimatedBuilder(
- animation: widget.route._ignorePointerNotifier,
+ animation: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false),
builder: (BuildContext context, Widget? child) {
- focusScopeNode.canRequestFocus = !_shouldIgnoreFocusRequest;
+ final bool ignoreEvents = _shouldIgnoreFocusRequest;
+ focusScopeNode.canRequestFocus = !ignoreEvents;
return IgnorePointer(
- ignoring: widget.route._ignorePointer,
+ ignoring: ignoreEvents,
child: child,
);
},
@@ -1154,36 +1140,11 @@
return child;
}
- /// Whether this route should ignore pointers when transitions are in progress.
- ///
- /// Pointers always are ignored when [isCurrent] is false (e.g., when a route
- /// has a new route pushed on top of it, or during a route's exit transition
- /// after popping). Override this value to also ignore pointers on pages during
- /// transitions where this route is the current route (e.g., after the route
- /// above this route pops, or during this route's entrance transition).
- ///
- /// Returns false by default.
- ///
- /// See also:
- ///
- /// * [CupertinoRouteTransitionMixin], [CupertinoModalPopupRoute], and
- /// [CupertinoDialogRoute], which use this property to specify that
- /// Cupertino routes ignore pointers during transitions.
- @protected
- bool get ignorePointerDuringTransitions => false;
-
@override
void install() {
super.install();
- _animationProxy = ProxyAnimation(super.animation)
- ..addStatusListener(_handleAnimationStatusChanged);
- _secondaryAnimationProxy = ProxyAnimation(super.secondaryAnimation)
- ..addStatusListener(_handleAnimationStatusChanged);
- navigator!.userGestureInProgressNotifier.addListener(_maybeUpdateIgnorePointer);
- }
-
- void _handleAnimationStatusChanged(AnimationStatus status) {
- _maybeUpdateIgnorePointer();
+ _animationProxy = ProxyAnimation(super.animation);
+ _secondaryAnimationProxy = ProxyAnimation(super.secondaryAnimation);
}
@override
@@ -1419,19 +1380,6 @@
Animation<double>? get secondaryAnimation => _secondaryAnimationProxy;
ProxyAnimation? _secondaryAnimationProxy;
- bool get _ignorePointer => _ignorePointerNotifier.value;
- final ValueNotifier<bool> _ignorePointerNotifier = ValueNotifier<bool>(false);
-
- void _maybeUpdateIgnorePointer() {
- bool isTransitioning(Animation<double>? animation) {
- return animation?.status == AnimationStatus.forward || animation?.status == AnimationStatus.reverse;
- }
- _ignorePointerNotifier.value = !isCurrent ||
- (navigator?.userGestureInProgress ?? false) ||
- (ignorePointerDuringTransitions &&
- (isTransitioning(animation) || isTransitioning(secondaryAnimation)));
- }
-
final List<WillPopCallback> _willPopCallbacks = <WillPopCallback>[];
/// Returns [RoutePopDisposition.doNotPop] if any of callbacks added with
@@ -1650,14 +1598,9 @@
child: barrier,
);
}
- barrier = AnimatedBuilder(
- animation: _ignorePointerNotifier,
- builder: (BuildContext context, Widget? child) {
- return IgnorePointer(
- ignoring: _ignorePointer,
- child: child,
- );
- },
+ barrier = IgnorePointer(
+ ignoring: animation!.status == AnimationStatus.reverse || // changedInternalState is called when animation.status updates
+ animation!.status == AnimationStatus.dismissed, // dismissed is possible when doing a manual pop gesture
child: barrier,
);
if (semanticsDismissible && barrierDismissible) {
diff --git a/packages/flutter/test/cupertino/action_sheet_test.dart b/packages/flutter/test/cupertino/action_sheet_test.dart
index c9b6aa0..1b849c9 100644
--- a/packages/flutter/test/cupertino/action_sheet_test.dart
+++ b/packages/flutter/test/cupertino/action_sheet_test.dart
@@ -29,13 +29,13 @@
);
await tester.tap(find.text('Go'));
- await tester.pumpAndSettle();
+ await tester.pump();
- expect(find.byType(CupertinoActionSheet), findsOneWidget);
+ expect(find.text('Action Sheet'), findsOneWidget);
- await tester.tap(find.byType(ModalBarrier).last);
- await tester.pumpAndSettle();
- expect(find.byType(CupertinoActionSheet), findsNothing);
+ await tester.tapAt(const Offset(20.0, 20.0));
+ await tester.pump();
+ expect(find.text('Action Sheet'), findsNothing);
});
testWidgets('Verify that a tap on title section (not buttons) does not dismiss an action sheet', (WidgetTester tester) async {
@@ -867,7 +867,7 @@
expect(find.byType(CupertinoActionSheet), findsNothing);
});
- testWidgets('Modal barrier cannot be dismissed during transition', (WidgetTester tester) async {
+ testWidgets('Modal barrier is pressed during transition', (WidgetTester tester) async {
await tester.pumpWidget(
createAppWithButtonThatLaunchesActionSheet(
CupertinoActionSheet(
@@ -906,20 +906,21 @@
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, moreOrLessEquals(337.1, epsilon: 0.1));
- // Attempt to dismiss
+ // Exit animation
await tester.tapAt(const Offset(20.0, 20.0));
await tester.pump(const Duration(milliseconds: 60));
- // Enter animation is continuing
- expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, moreOrLessEquals(325.4, epsilon: 0.1));
+ await tester.pump(const Duration(milliseconds: 60));
+ expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, moreOrLessEquals(374.3, epsilon: 0.1));
- await tester.pumpAndSettle();
+ await tester.pump(const Duration(milliseconds: 60));
+ expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, moreOrLessEquals(470.0, epsilon: 0.1));
- // Attempt to dismiss again
- await tester.tapAt(const Offset(20.0, 20.0));
- await tester.pumpAndSettle();
+ await tester.pump(const Duration(milliseconds: 60));
+ expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
// Action sheet has disappeared
+ await tester.pump(const Duration(milliseconds: 60));
expect(find.byType(CupertinoActionSheet), findsNothing);
});
@@ -951,7 +952,7 @@
);
await tester.tap(find.text('Go'));
- await tester.pumpAndSettle();
+ await tester.pump();
expect(
semantics,
diff --git a/packages/flutter/test/cupertino/dialog_test.dart b/packages/flutter/test/cupertino/dialog_test.dart
index dc2679a..680cdbc 100644
--- a/packages/flutter/test/cupertino/dialog_test.dart
+++ b/packages/flutter/test/cupertino/dialog_test.dart
@@ -1074,8 +1074,6 @@
transition = tester.firstWidget(fadeTransitionFinder);
expect(transition.opacity.value, moreOrLessEquals(1.0, epsilon: 0.001));
- await tester.pumpAndSettle();
-
await tester.tap(find.text('Delete'));
// Exit animation, look at reverse FadeTransition.
diff --git a/packages/flutter/test/cupertino/route_test.dart b/packages/flutter/test/cupertino/route_test.dart
index 6884a53..a60a7b6 100644
--- a/packages/flutter/test/cupertino/route_test.dart
+++ b/packages/flutter/test/cupertino/route_test.dart
@@ -442,8 +442,6 @@
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(0.0, epsilon: 0.1));
- await tester.pumpAndSettle();
-
// Exit animation
await tester.tap(find.text('Close'));
await tester.pump();
@@ -549,8 +547,6 @@
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-267.0, epsilon: 1.0));
- await tester.pumpAndSettle();
-
// Exit animation
await tester.tap(find.text('Close'));
await tester.pump();
@@ -640,8 +636,6 @@
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, 0.0);
- await tester.pumpAndSettle();
-
// Exit animation
await tester.tap(find.text('Close'));
await tester.pump();
@@ -661,221 +655,6 @@
await testNoParallax(tester, fromFullscreenDialog: true);
});
- group('Route interactivity during transition animations', () {
- testWidgets('CupertinoPageRoute ignores pointers when route on top of it pops', (WidgetTester tester) async {
- final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
- bool homeTapped = false;
- await tester.pumpWidget(
- CupertinoApp(
- navigatorKey: navigatorKey,
- home: TextButton(
- onPressed: () => homeTapped = true,
- child: const Text('Home'),
- ),
- ),
- );
-
- navigatorKey.currentState!.push<void>(
- CupertinoPageRoute<void>(
- builder: (_) => const Text('Page 2'),
- )
- );
- await tester.pumpAndSettle();
-
- expect(find.text('Page 2'), findsOneWidget);
-
- navigatorKey.currentState!.pop();
- await tester.pump(const Duration(milliseconds: 100));
-
- expect(find.text('Page 2'), findsOneWidget); // Transition still in progress
-
- await tester.tap(find.text('Home'), warnIfMissed: false); // Home route is not tappable
- expect(homeTapped, false);
-
- await tester.pumpAndSettle(); // Transition completes
-
- await tester.tap(find.text('Home'));
- expect(homeTapped, true);
- });
-
- testWidgets('fullscreenDialog CupertinoPageRoute ignores pointers when route on top of it pops', (WidgetTester tester) async {
- bool homeTapped = false;
- await tester.pumpWidget(
- CupertinoApp(
- home: TextButton(
- onPressed: () => homeTapped = true,
- child: const Text('Home'),
- ),
- ),
- );
-
- tester.state<NavigatorState>(find.byType(Navigator)).push<void>(
- CupertinoPageRoute<void>(
- fullscreenDialog: true,
- builder: (_) => const Text('Page 2'),
- )
- );
- await tester.pumpAndSettle();
-
- expect(find.text('Page 2'), findsOneWidget);
-
- tester.state<NavigatorState>(find.byType(Navigator)).pop();
- await tester.pump(const Duration(milliseconds: 100));
-
- expect(find.text('Page 2'), findsOneWidget); // Transition still in progress
-
- await tester.tap(find.text('Home'), warnIfMissed: false); // Home route is not tappable
- expect(homeTapped, false);
-
- await tester.pumpAndSettle(); // Transition completes
-
- await tester.tap(find.text('Home'));
- expect(homeTapped, true);
- });
-
- testWidgets('CupertinoPageRoute ignores pointers when user pop gesture is in progress', (WidgetTester tester) async {
- bool homeTapped = false;
- await tester.pumpWidget(
- CupertinoApp(
- home: TextButton(
- onPressed: () => homeTapped = true,
- child: const Text('Page 1'),
- ),
- ),
- );
-
- tester.state<NavigatorState>(find.byType(Navigator)).push(
- CupertinoPageRoute<void>(
- builder: (_) => const Text('Page 2'),
- ),
- );
- await tester.pumpAndSettle();
-
- expect(find.text('Page 1'), findsNothing);
-
- final TestGesture swipeGesture = await tester.startGesture(const Offset(5, 100));
-
- await swipeGesture.moveBy(const Offset(100, 0));
- await tester.pump();
-
- expect(find.text('Page 1'), findsOneWidget);
- expect(tester.state<NavigatorState>(find.byType(Navigator)).userGestureInProgress, true);
-
- await tester.tap(find.text('Page 1'), warnIfMissed: false);
- expect(homeTapped, false);
- });
-
- testWidgets('CupertinoPageRoute ignores pointers when it is pushed on top of other route', (WidgetTester tester) async {
- await tester.pumpWidget(
- CupertinoApp(
- onGenerateRoute: (_) => CupertinoPageRoute<void>(
- builder: (_) => const Text('Home'),
- ),
- ),
- );
-
- await tester.tap(find.text('Home'));
- tester.state<NavigatorState>(find.byType(Navigator)).push(
- CupertinoPageRoute<void>(
- builder: (_) => const Text('Page 2'),
- ),
- );
- await tester.pump();
- await tester.pump(const Duration(milliseconds: 100));
-
- expect(find.text('Home'), findsOneWidget); // Transition still in progress
-
- // Can't test directly for taps because route is interactive but offstage
- // One ignore pointer for each of two overlay entries (ModalScope, ModalBarrier) on each of two routes
- expect(find.byType(IgnorePointer, skipOffstage: false), findsNWidgets(4));
- final List<Element> ignorePointers = find.byType(IgnorePointer, skipOffstage: false).evaluate().toList();
- expect((ignorePointers.first.widget as IgnorePointer).ignoring, true); // Home modalBarrier
- expect((ignorePointers[1].widget as IgnorePointer).ignoring, true); // Home modalScope
- expect((ignorePointers[2].widget as IgnorePointer).ignoring, true); // Page 2 modalBarrier
- expect((ignorePointers.last.widget as IgnorePointer).ignoring, true); // Page 2 modalScope
- });
-
- testWidgets('showCupertinoDialog ignores pointers until transition completes', (WidgetTester tester) async {
- await tester.pumpWidget(
- CupertinoApp(
- home: Builder(
- builder: (BuildContext context) {
- return TextButton(
- onPressed: () {
- showCupertinoModalPopup<void>(
- context: context,
- builder: (BuildContext innerContext) => TextButton(
- onPressed: Navigator.of(innerContext).pop,
- child: const Text('dialog'),
- ),
- );
- },
- child: const Text('Show Dialog'),
- );
- },
- ),
- ),
- );
-
- // Open the dialog.
- await tester.tap(find.byType(TextButton));
- await tester.pump(const Duration(milliseconds: 100));
-
- // Trigger pop while the transition is in progress
- await tester.tap(find.text('dialog'), warnIfMissed: false);
- await tester.pumpAndSettle();
-
- // Transition is over and the dialog has not been dismissed
- expect(find.text('dialog'), findsOneWidget);
-
- await tester.tap(find.text('dialog'));
- await tester.pumpAndSettle();
-
- // The dialog has not been dismissed
- expect(find.text('dialog'), findsNothing);
- });
-
- testWidgets('showCupertinoModalPopup ignores pointers until transition completes', (WidgetTester tester) async {
- await tester.pumpWidget(
- CupertinoApp(
- home: Builder(
- builder: (BuildContext context) {
- return TextButton(
- onPressed: () {
- showCupertinoModalPopup<void>(
- context: context,
- builder: (BuildContext innerContext) => TextButton(
- onPressed: Navigator.of(innerContext).pop,
- child: const Text('modal'),
- ),
- );
- },
- child: const Text('Show modal'),
- );
- },
- ),
- ),
- );
-
- // Open the modal popup
- await tester.tap(find.byType(TextButton));
- await tester.pump(const Duration(milliseconds: 100));
-
- // Trigger pop while the transition is in progress
- await tester.tap(find.text('modal'), warnIfMissed: false);
- await tester.pumpAndSettle();
-
- // Transition is over and the dialog has not been dismissed
- expect(find.text('modal'), findsOneWidget);
-
- await tester.tap(find.text('modal'));
- await tester.pumpAndSettle();
-
- // The dialog has not been dismissed
- expect(find.text('modal'), findsNothing);
- });
- });
-
testWidgets('Animated push/pop is not linear', (WidgetTester tester) async {
await tester.pumpWidget(
const CupertinoApp(
diff --git a/packages/flutter/test/material/page_test.dart b/packages/flutter/test/material/page_test.dart
index 6082cd6..42b63de 100644
--- a/packages/flutter/test/material/page_test.dart
+++ b/packages/flutter/test/material/page_test.dart
@@ -432,7 +432,6 @@
routes: routes,
),
);
- expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 2);
await tester.tap(find.text('PUSH'));
expect(await tester.pumpAndSettle(const Duration(minutes: 1)), 2);
expect(find.text('PUSH'), findsNothing);
@@ -791,8 +790,8 @@
// Tapping the "page" route's back button doesn't do anything either.
await tester.tap(find.byTooltip('Back'), warnIfMissed: false);
- await tester.pump();
- expect(tester.getTopLeft(find.byKey(pageScaffoldKey, skipOffstage: false)), const Offset(400, 0));
+ await tester.pumpAndSettle();
+ expect(tester.getTopLeft(find.byKey(pageScaffoldKey)), const Offset(400, 0));
expect(tester.getTopLeft(find.byKey(homeScaffoldKey)).dx, lessThan(0));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
diff --git a/packages/flutter/test/widgets/routes_test.dart b/packages/flutter/test/widgets/routes_test.dart
index 1120615..ad36da4 100644
--- a/packages/flutter/test/widgets/routes_test.dart
+++ b/packages/flutter/test/widgets/routes_test.dart
@@ -940,36 +940,6 @@
expect(trainHopper2.currentTrain, isNull); // Has been disposed.
});
- testWidgets('secondary animation is AnimationMin when transition route that cannot be transitioned to or from pops', (WidgetTester tester) async {
- final PageRoute<void> pageRouteOne = MaterialPageRoute<void>(
- builder: (_) => const Text('Page One'),
- );
-
- await tester.pumpWidget(
- MaterialApp(
- onGenerateRoute: (_) => pageRouteOne,
- ),
- );
-
- final PageRoute<void> pageRouteTwo = MaterialPageRoute<void>(
- fullscreenDialog: true,
- builder: (_) => const Text('Page Two'),
- );
-
- tester.state<NavigatorState>(find.byType(Navigator)).push(pageRouteTwo);
- await tester.pumpAndSettle();
-
- tester.state<NavigatorState>(find.byType(Navigator)).pop();
- await tester.pump();
-
- expect(
- ((pageRouteOne.secondaryAnimation! as ProxyAnimation).parent! as ProxyAnimation).parent,
- isA<AnimationMin<double>>()
- ..having((AnimationMin<double> a) => a.first, 'first', equals(kAlwaysDismissedAnimation))
- ..having((AnimationMin<double> a) => a.next, 'first', equals(pageRouteTwo.animation)),
- );
- });
-
testWidgets('secondary animation is triggered when pop initial route', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
late Animation<double> secondaryAnimationOfRouteOne;
@@ -1041,41 +1011,6 @@
expect(find.byType(ModalBarrier), findsNWidgets(1));
});
- testWidgets('showGeneralDialog ModalBarrier does not ignore pointers during transitions', (WidgetTester tester) async {
- await tester.pumpWidget(
- MaterialApp(
- home: Builder(
- builder: (BuildContext context) {
- return TextButton(
- onPressed: () {
- showGeneralDialog<void>(
- context: context,
- transitionDuration: const Duration(milliseconds: 400),
- pageBuilder: (BuildContext innerContext, __, ___) => TextButton(
- onPressed: Navigator.of(innerContext).pop,
- child: const Text('dialog'),
- ),
- );
- },
- child: const Text('Show Dialog'),
- );
- },
- ),
- ),
- );
-
- // Open the dialog.
- await tester.tap(find.byType(TextButton));
- await tester.pump(const Duration(milliseconds: 200));
-
- // Trigger pop while the transition is in progress
- await tester.tap(find.text('dialog'));
- await tester.pumpAndSettle();
-
- // The dialog has been dismissed mid-transition
- expect(find.text('dialog'), findsNothing);
- });
-
testWidgets('showGeneralDialog adds non-dismissible barrier when barrierDismissible is false', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Builder(
@@ -1371,67 +1306,6 @@
});
});
- testWidgets('does not ignore pointers when route on top of it pops', (WidgetTester tester) async {
- await tester.pumpWidget(
- MaterialApp(
- theme: ThemeData(
- pageTransitionsTheme: const PageTransitionsTheme(
- builders: <TargetPlatform, PageTransitionsBuilder>{
- // Use a transitions builder that will keep the underlying content
- // partially visible during a transition
- TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
- },
- )
- ),
- home: const Text('Home'),
- ),
- );
-
- tester.state<NavigatorState>(find.byType(Navigator)).push<void>(
- MaterialPageRoute<void>(builder: (_) => const Text('Page 2'))
- );
-
- await tester.pumpAndSettle();
- expect(find.text('Page 2'), findsOneWidget);
-
- tester.state<NavigatorState>(find.byType(Navigator)).pop();
- await tester.pump(const Duration(milliseconds: 100));
-
- expect(find.text('Page 2'), findsOneWidget); // Transition still in progress
-
- await tester.tap(find.text('Home')); // Home route is tappable
- });
-
- testWidgets('does not ignore pointers during its own entrance animation', (WidgetTester tester) async {
- await tester.pumpWidget(
- MaterialApp(
- onGenerateRoute: (_) => MaterialPageRoute<void>(
- builder: (_) => const Text('Home'),
- ),
- ),
- );
-
- await tester.tap(find.text('Home'));
- tester.state<NavigatorState>(find.byType(Navigator)).push(
- MaterialPageRoute<void>(
- builder: (_) => const Text('Page 2'),
- ),
- );
- await tester.pump();
- await tester.pump(const Duration(milliseconds: 100));
-
- expect(find.text('Home'), findsOneWidget); // Transition still in progress
-
- // Can't test directly for taps because route is interactive but offstage
- // One ignore pointer for each of two overlay entries (ModalScope, ModalBarrier) on each of two routes
- expect(find.byType(IgnorePointer, skipOffstage: false), findsNWidgets(4));
- final List<Element> ignorePointers = find.byType(IgnorePointer, skipOffstage: false).evaluate().toList();
- expect((ignorePointers.first.widget as IgnorePointer).ignoring, true); // Home modalBarrier
- expect((ignorePointers[1].widget as IgnorePointer).ignoring, true); // Home modalScope
- expect((ignorePointers[2].widget as IgnorePointer).ignoring, false); // Page 2 modalBarrier
- expect((ignorePointers.last.widget as IgnorePointer).ignoring, false); // Page 2 modalScope
- });
-
testWidgets('reverseTransitionDuration defaults to transitionDuration', (WidgetTester tester) async {
final GlobalKey containerKey = GlobalKey();
diff --git a/packages/flutter_test/test/widget_tester_test.dart b/packages/flutter_test/test/widget_tester_test.dart
index 6f1a917..c10992f 100644
--- a/packages/flutter_test/test/widget_tester_test.dart
+++ b/packages/flutter_test/test/widget_tester_test.dart
@@ -440,7 +440,7 @@
await tester.tap(find.text('Next'));
await tester.pump();
- await tester.pumpAndSettle();
+ await tester.pump(const Duration(milliseconds: 400));
await tester.pageBack();
await tester.pump();