From 9f6b05d32ba6dcad45f536d7ff0a971ada525b19 Mon Sep 17 00:00:00 2001 From: zhuhua Date: Fri, 28 Aug 2026 17:59:49 +0800 Subject: [PATCH] Don't trigger predictive back transition on a route covered by a dragged CupertinoSheet PredictiveBackPageTransitionsBuilder and PredictiveBackFullscreenPageTransitionsBuilder chose whether to show the predictive back transition based on route.popGestureInProgress, which is navigator.userGestureInProgress -- true for any user gesture anywhere in the navigator, including dragging a CupertinoSheet that covers the route. That made the covered route incorrectly switch to the predictive back transition while an unrelated sheet was being dragged. Gate on the predictive back phase instead, which is only non-idle while an actual predictive back gesture is in progress. --- ...dictive_back_page_transitions_builder.dart | 13 ++-- ...2026_08_28_predictive_back_sheet_drag.yaml | 3 + ...ve_back_page_transitions_builder_test.dart | 65 +++++++++++++++++++ 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 packages/material_ui/pending_changelogs/change_2026_08_28_predictive_back_sheet_drag.yaml diff --git a/packages/material_ui/lib/src/predictive_back_page_transitions_builder.dart b/packages/material_ui/lib/src/predictive_back_page_transitions_builder.dart index eb235c261963..67ee30be124c 100644 --- a/packages/material_ui/lib/src/predictive_back_page_transitions_builder.dart +++ b/packages/material_ui/lib/src/predictive_back_page_transitions_builder.dart @@ -76,11 +76,8 @@ class PredictiveBackPageTransitionsBuilder extends PageTransitionsBuilder { PredictiveBackEvent? startBackEvent, PredictiveBackEvent? currentBackEvent, ) { - // Only do a predictive back transition when the user is performing a - // pop gesture. Otherwise, for things like button presses or other - // programmatic navigation, fall back to - // FadeForwardsPageTransitionsBuilder. - if (route.popGestureInProgress) { + // Only use the predictive back transition during an active back gesture. + if (phase != _PredictiveBackPhase.idle) { return _PredictiveBackSharedElementPageTransition( isDelegatedTransition: true, animation: animation, @@ -156,10 +153,8 @@ class PredictiveBackFullscreenPageTransitionsBuilder extends PageTransitionsBuil PredictiveBackEvent? startBackEvent, PredictiveBackEvent? currentBackEvent, ) { - // Only do a predictive back transition when the user is performing a - // pop gesture. Otherwise, for things like button presses or other - // programmatic navigation, fall back to ZoomPageTransitionsBuilder. - if (route.popGestureInProgress) { + // Only use the predictive back transition during an active back gesture. + if (phase != _PredictiveBackPhase.idle) { return _PredictiveBackFullscreenPageTransition( animation: animation, secondaryAnimation: secondaryAnimation, diff --git a/packages/material_ui/pending_changelogs/change_2026_08_28_predictive_back_sheet_drag.yaml b/packages/material_ui/pending_changelogs/change_2026_08_28_predictive_back_sheet_drag.yaml new file mode 100644 index 000000000000..a2cc18f263b2 --- /dev/null +++ b/packages/material_ui/pending_changelogs/change_2026_08_28_predictive_back_sheet_drag.yaml @@ -0,0 +1,3 @@ +changelog: | + - Prevents a covered route from entering a predictive back transition while dragging a `CupertinoSheet`. +version: patch diff --git a/packages/material_ui/test/predictive_back_page_transitions_builder_test.dart b/packages/material_ui/test/predictive_back_page_transitions_builder_test.dart index 446a26e80e49..c8ff788ee73f 100644 --- a/packages/material_ui/test/predictive_back_page_transitions_builder_test.dart +++ b/packages/material_ui/test/predictive_back_page_transitions_builder_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:cupertino_ui/cupertino_ui.dart' show CupertinoPageScaffold, showCupertinoSheet; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -673,6 +674,70 @@ void main() { findsOneWidget, ); }); + + testWidgets( + 'route covered by a CupertinoSheet does not switch to the predictive back ' + 'transition while the sheet is dragged (${pageTransitionsBuilder.runtimeType})', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + pageTransitionsTheme: PageTransitionsTheme( + builders: { + for (final TargetPlatform platform in TargetPlatform.values) + platform: pageTransitionsBuilder, + }, + ), + ), + home: Builder( + builder: (BuildContext context) { + return Scaffold( + body: Center( + child: TextButton( + onPressed: () { + showCupertinoSheet( + context: context, + scrollableBuilder: (BuildContext context, ScrollController controller) { + return CupertinoPageScaffold( + child: ListView.builder( + controller: controller, + itemCount: 30, + itemBuilder: (BuildContext context, int index) { + return SizedBox(height: 56.0, child: Text('item $index')); + }, + ), + ); + }, + ); + }, + child: const Text('open sheet'), + ), + ), + ); + }, + ), + ), + ); + + await tester.tap(find.text('open sheet')); + await tester.pumpAndSettle(); + + expect(_findPredictiveBackPageTransition(pageTransitionsBuilder), findsNothing); + expect(_findFallbackPageTransition(pageTransitionsBuilder), findsOneWidget); + + final TestGesture gesture = await tester.startGesture(const Offset(100, 300)); + // A small drag first wins the gesture arena before the larger drag. + await gesture.moveBy(const Offset(0, 30)); + await gesture.moveBy(const Offset(0, 100)); + await tester.pump(); + + expect(_findPredictiveBackPageTransition(pageTransitionsBuilder), findsNothing); + expect(_findFallbackPageTransition(pageTransitionsBuilder), findsOneWidget); + + await gesture.up(); + await tester.pumpAndSettle(); + }, + ); } testWidgets('PredictiveBackPageTransitionsBuilder uses fallbackColor', (