diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index b48f25d4b3a..f873ee588b4 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -1583,7 +1583,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute secondaryAnimation, Widget child, ) { - if (receivedTransition == null) { + if (receivedTransition == null || secondaryAnimation.isDismissed) { return buildTransitions(context, animation, secondaryAnimation, child); } diff --git a/packages/flutter/test/cupertino/sheet_test.dart b/packages/flutter/test/cupertino/sheet_test.dart index 42a671a76b0..5e2005fdd2e 100644 --- a/packages/flutter/test/cupertino/sheet_test.dart +++ b/packages/flutter/test/cupertino/sheet_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import '../widgets/navigator_utils.dart'; @@ -810,8 +811,73 @@ void main() { await tester.pumpAndSettle(); final Finder clipRRectFinder = find.byType(ClipRRect); - final Rect clipRect = tester.getRect(clipRRectFinder); - expect(clipRect.center, equals(const Offset(400, 300))); + expect(clipRRectFinder, findsNothing); + }); + + testWidgets('Sheet transition does not interfere after popping', (WidgetTester tester) async { + final GlobalKey homeKey = GlobalKey(); + final GlobalKey sheetKey = GlobalKey(); + final GlobalKey popupMenuButtonKey = GlobalKey(); + + await tester.pumpWidget( + CupertinoApp( + localizationsDelegates: const >[ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + home: CupertinoPageScaffold( + key: homeKey, + child: CupertinoListTile( + onTap: () { + showCupertinoSheet( + context: homeKey.currentContext!, + pageBuilder: (BuildContext context) { + return CupertinoPageScaffold( + key: sheetKey, + child: const Center(child: Text('Page 2')), + ); + }, + ); + }, + title: const Text('ListItem 0'), + trailing: Material( + type: MaterialType.transparency, + child: PopupMenuButton( + key: popupMenuButtonKey, + itemBuilder: (BuildContext context) { + return >[ + const PopupMenuItem(child: Text('Item 0')), + const PopupMenuItem(child: Text('Item 1')), + ]; + }, + ), + ), + ), + ), + ), + ); + + await tester.tap(find.text('ListItem 0')); + await tester.pumpAndSettle(); + + expect(find.text('Page 2'), findsOneWidget); + + final TestGesture gesture = await tester.startGesture(const Offset(100, 200)); + await gesture.moveBy(const Offset(0, 350)); + await tester.pump(); + + await gesture.up(); + await tester.pumpAndSettle(); + + expect(find.text('Page 2'), findsNothing); + expect(find.text('ListItem 0'), findsOneWidget); + + await tester.tap(find.byKey(popupMenuButtonKey)); + await tester.pumpAndSettle(); + + expect(find.text('Item 0'), findsOneWidget); + expect(tester.takeException(), isNull); }); group('drag dismiss gesture', () { diff --git a/packages/flutter/test/widgets/routes_test.dart b/packages/flutter/test/widgets/routes_test.dart index 92ffb7a2da7..ef27822b90d 100644 --- a/packages/flutter/test/widgets/routes_test.dart +++ b/packages/flutter/test/widgets/routes_test.dart @@ -766,6 +766,52 @@ void main() { expect(secondaryAnimationPageOne.parent, kAlwaysDismissedAnimation); }); + testWidgets( + 'delegated transitions are removed when secondary animation is dismissed and next route is removed', + (WidgetTester tester) async { + final GlobalKey navigator = GlobalKey(); + await tester.pumpWidget( + MaterialApp( + navigatorKey: navigator, + theme: ThemeData( + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: CupertinoPageTransitionsBuilder(), + }, + ), + ), + home: const Text('home'), + ), + ); + + // Push first page with custom transition builder. + final Route firstRoute = CupertinoSheetRoute( + builder: (_) { + return const Text('Page One'); + }, + ); + + navigator.currentState!.push(firstRoute); + await tester.pumpAndSettle(); + + expect(find.text('Page One'), findsOneWidget); + final Finder cupertinoSheetDelegatedTransitionFinder = find.ancestor( + of: find.ancestor(of: find.byType(ClipRRect), matching: find.byType(AnimatedBuilder)), + matching: find.byType(ScaleTransition), + ); + expect(cupertinoSheetDelegatedTransitionFinder, findsOneWidget); + + navigator.currentState!.pop(); + await tester.pumpAndSettle(); + + // Verify home is still visible without transitions. + expect(find.text('home'), findsOneWidget); + + // Verify the delegated transition is removed. + expect(cupertinoSheetDelegatedTransitionFinder, findsNothing); + }, + ); + testWidgets('secondary animation is kDismissed after train hopping finishes and pop', ( WidgetTester tester, ) async {