Suppress previous route transition if current route is fullscreenDialog (#159312)

Fixes #159289

Cupertino and Material routes were looking to see if the next route was
a full screen dialog route, and not transitioning accordingly. However
with the updates to allow [mixed transitions in an
app](https://github.com/flutter/flutter/pull/150031), any route will try
and transition if a delegatedTransition is available. This PR makes it
so that Cupertino and Material routes that are fullscreen dialogs will
use `canTransitionFrom` to tell the previous route not to transition.

Before:

[388677067-d301238d-6615-42a7-b60a-611c61136d88.webm](https://github.com/user-attachments/assets/eea99ee9-0bc8-4981-a950-08f99a7fdb3e)

After:


https://github.com/user-attachments/assets/7deb5143-cd67-4696-95ec-9d7df329dfa4



## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart
index 390bac8..ae68440 100644
--- a/packages/flutter/lib/src/cupertino/route.dart
+++ b/packages/flutter/lib/src/cupertino/route.dart
@@ -176,6 +176,12 @@
   }
 
   @override
+  bool canTransitionFrom(TransitionRoute<dynamic> previousRoute) {
+    // Supress previous route from transitioning if this is a fullscreenDialog route.
+    return previousRoute is PageRoute && !fullscreenDialog;
+  }
+
+  @override
   Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
     final Widget child = buildContent(context);
     return Semantics(
diff --git a/packages/flutter/lib/src/material/page.dart b/packages/flutter/lib/src/material/page.dart
index 2b87aaa..5a5fe81 100644
--- a/packages/flutter/lib/src/material/page.dart
+++ b/packages/flutter/lib/src/material/page.dart
@@ -125,6 +125,12 @@
   }
 
   @override
+  bool canTransitionFrom(TransitionRoute<dynamic> previousRoute) {
+    // Supress previous route from transitioning if this is a fullscreenDialog route.
+    return previousRoute is PageRoute && !fullscreenDialog;
+  }
+
+  @override
   Widget buildPage(
     BuildContext context,
     Animation<double> animation,
diff --git a/packages/flutter/test/cupertino/route_test.dart b/packages/flutter/test/cupertino/route_test.dart
index 182bf0b..fa71528 100644
--- a/packages/flutter/test/cupertino/route_test.dart
+++ b/packages/flutter/test/cupertino/route_test.dart
@@ -2691,6 +2691,54 @@
     await tester.pumpAndSettle();
   });
 
+  testWidgets('fullscreen routes do not transition previous route', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      CupertinoApp(
+        initialRoute: '/',
+        onGenerateRoute: (RouteSettings settings) {
+          if (settings.name == '/') {
+            return PageRouteBuilder<void>(
+              pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
+                return CupertinoPageScaffold(
+                  navigationBar: const CupertinoNavigationBar(
+                    middle: Text('Page 1'),
+                  ),
+                  child: Container()
+                );
+              },
+            );
+          }
+          return CupertinoPageRoute<void>(
+            builder: (BuildContext context) {
+              return CupertinoPageScaffold(
+                navigationBar: const CupertinoNavigationBar(
+                  middle: Text('Page 2'),
+                ),
+                child: Container(),
+              );
+            },
+            fullscreenDialog: true,
+          );
+        },
+      ),
+    );
+
+    expect(find.text('Page 1'), findsOneWidget);
+    expect(find.text('Page 2'), findsNothing);
+
+    final double pageTitleDX = tester.getTopLeft(find.text('Page 1')).dx;
+
+    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
+    await tester.pump();
+    await tester.pump(const Duration(milliseconds: 100));
+
+    // Second page transition has started.
+    expect(find.text('Page 2'), findsOneWidget);
+
+    // First page has not moved.
+    expect(tester.getTopLeft(find.text('Page 1')).dx, equals(pageTitleDX));
+  });
+
   testWidgets('Setting CupertinoDialogRoute.requestFocus to false does not request focus on the dialog', (WidgetTester tester) async {
     late BuildContext savedContext;
     final FocusNode focusNode = FocusNode();
diff --git a/packages/flutter/test/material/page_test.dart b/packages/flutter/test/material/page_test.dart
index 7ae339e..26c3cfe 100644
--- a/packages/flutter/test/material/page_test.dart
+++ b/packages/flutter/test/material/page_test.dart
@@ -656,6 +656,54 @@
     expect(tester.getTopLeft(find.text('Page 2')), Offset.zero);
   }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS,  TargetPlatform.macOS }));
 
+  testWidgets('test fullscreen routes do not transition previous route', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      MaterialApp(
+        initialRoute: '/',
+        onGenerateRoute: (RouteSettings settings) {
+          if (settings.name == '/') {
+            return PageRouteBuilder<void>(
+              pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
+                return Scaffold(
+                  appBar: AppBar(
+                    title: const Text('Page 1'),
+                  ),
+                  body: Container()
+                );
+              },
+            );
+          }
+          return MaterialPageRoute<void>(
+            builder: (BuildContext context) {
+              return Scaffold(
+                appBar: AppBar(
+                  title: const Text('Page 2'),
+                ),
+                body: Container(),
+              );
+            },
+            fullscreenDialog: true,
+          );
+        },
+      ),
+    );
+
+    expect(find.text('Page 1'), findsOneWidget);
+    expect(find.text('Page 2'), findsNothing);
+
+    final double pageTitleDX = tester.getTopLeft(find.text('Page 1')).dx;
+
+    tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
+    await tester.pump();
+    await tester.pump(const Duration(milliseconds: 100));
+
+    // Second page transition has started.
+    expect(find.text('Page 2'), findsOneWidget);
+
+    // First page has not moved.
+    expect(tester.getTopLeft(find.text('Page 1')).dx, equals(pageTitleDX));
+  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS,  TargetPlatform.macOS }));
+
   testWidgets('test adaptable transitions switch during execution', (WidgetTester tester) async {
     await tester.pumpWidget(
       MaterialApp(