diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart index 5ce4479480e..2551c6613ae 100644 --- a/packages/flutter/lib/src/cupertino/route.dart +++ b/packages/flutter/lib/src/cupertino/route.dart @@ -201,7 +201,7 @@ mixin CupertinoRouteTransitionMixin on PageRoute { // gesture is detected. The returned controller handles all of the subsequent // drag events. static _CupertinoBackGestureController _startPopGesture(PageRoute route) { - assert(route.popGestureEnabled); + assert(route.popGestureEnabled || route.popGestureEnabled_); return _CupertinoBackGestureController( navigator: route.navigator!, @@ -252,6 +252,7 @@ mixin CupertinoRouteTransitionMixin on PageRoute { linearTransition: linearTransition, child: _CupertinoBackGestureDetector( enabledCallback: () => route.popGestureEnabled, + popGestureEnabled_: () => route.popGestureEnabled_, onStartPopGesture: () => _startPopGesture(route), child: child, ), @@ -698,6 +699,7 @@ class _CupertinoBackGestureDetector extends StatefulWidget { const _CupertinoBackGestureDetector({ super.key, required this.enabledCallback, + required this.popGestureEnabled_, required this.onStartPopGesture, required this.child, }); @@ -706,6 +708,8 @@ class _CupertinoBackGestureDetector extends StatefulWidget { final ValueGetter enabledCallback; + final ValueGetter popGestureEnabled_; + final ValueGetter<_CupertinoBackGestureController> onStartPopGesture; @override @@ -720,7 +724,7 @@ class _CupertinoBackGestureDetectorState extends State<_CupertinoBackGestureD @override void initState() { super.initState(); - _recognizer = HorizontalDragGestureRecognizer(debugOwner: this) + _recognizer = _HorizontalDragGestureRecognizer(debugOwner: this) ..onStart = _handleDragStart ..onUpdate = _handleDragUpdate ..onEnd = _handleDragEnd @@ -776,7 +780,13 @@ class _CupertinoBackGestureDetectorState extends State<_CupertinoBackGestureD void _handlePointerDown(PointerDownEvent event) { if (widget.enabledCallback()) { - _recognizer.addPointer(event); + _recognizer + ..gestureSettings = null + ..addPointer(event); + } else if (widget.popGestureEnabled_()) { + _recognizer + ..gestureSettings = const DeviceGestureSettings(touchSlop: 28) + ..addPointer(event); } } @@ -883,7 +893,7 @@ class _CupertinoBackGestureController { } else { if (isCurrent) { // This route is destined to pop at this point. Reuse navigator's pop. - navigator.pop(); + navigator.pop(gesturePop); } // The popping may have finished inline if already at the target destination. @@ -1548,3 +1558,18 @@ class CupertinoDialogRoute extends RawDialogRoute { // transitions. static final Tween _dialogScaleTween = Tween(begin: 1.3, end: 1.0); } + +class _HorizontalDragGestureRecognizer extends HorizontalDragGestureRecognizer { + _HorizontalDragGestureRecognizer({ + super.debugOwner, + super.supportedDevices, + super.allowedButtonsFilter, + }); + + + @override + void didStopTrackingLastPointer(int pointer) { + gestureSettings = null; + super.didStopTrackingLastPointer(pointer); + } +} \ No newline at end of file diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index d121d10f1d6..92fa155c168 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -2521,6 +2521,7 @@ class ScaffoldState extends State final LocalHistoryEntry? entry = isPersistent ? null : LocalHistoryEntry( + popGestureEnabled: true, onRemove: () { if (!removedEntry && _currentBottomSheet?._widget == bottomSheet && !doingDispose) { removeCurrentBottomSheet(); diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index 5c4a8982617..47db0368a85 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -709,7 +709,11 @@ class LocalHistoryEntry { /// Creates an entry in the history of a [LocalHistoryRoute]. /// /// The [impliesAppBarDismissal] defaults to true if not provided. - LocalHistoryEntry({this.onRemove, this.impliesAppBarDismissal = true}); + LocalHistoryEntry({ + this.onRemove, + this.impliesAppBarDismissal = true, + this.popGestureEnabled = false, + }); /// Called when this entry is removed from the history of its associated [LocalHistoryRoute]. final VoidCallback? onRemove; @@ -722,6 +726,8 @@ class LocalHistoryEntry { /// Defaults to true. final bool impliesAppBarDismissal; + final bool popGestureEnabled; + /// Remove this entry from the history of its associated [LocalHistoryRoute]. void remove() { _owner?.removeLocalHistoryEntry(this); @@ -949,6 +955,14 @@ mixin LocalHistoryRoute on Route { @override bool didPop(T? result) { if (_localHistory != null && _localHistory!.isNotEmpty) { + if (result == gesturePop) { + for (var e in _localHistory!) { + e._owner = null; + } + _localHistory!.clear(); + _localHistory = null; + return super.didPop(result); + } final LocalHistoryEntry entry = _localHistory!.removeLast(); assert(entry._owner == this); entry._owner = null; @@ -970,6 +984,10 @@ mixin LocalHistoryRoute on Route { bool get willHandlePopInternally { return _localHistory != null && _localHistory!.isNotEmpty; } + + bool get popGestureEnabled_ { + return _localHistory?.lastOrNull?.popGestureEnabled ?? false; + } } class _DismissModalAction extends DismissAction { @@ -2839,3 +2857,9 @@ abstract class PopEntry { return 'PopEntry canPop: ${canPopNotifier.value}, onPopInvoked: $onPopInvokedWithResult'; } } + +class _GesturePop { + const _GesturePop(); +} + +const Object gesturePop = _GesturePop();