mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[CupertinoActionSheet] Support legacy buttons (#151136)
Fixes https://github.com/flutter/flutter/issues/150980 This PR allows buttons implemented with `GestureDetector.onTap` to be selected in the action sheet.
This commit is contained in:
parent
f50feec5be
commit
de04c13fdb
@ -546,21 +546,35 @@ class _SlidingTapGestureRecognizer extends VerticalDragGestureRecognizer {
|
||||
if (event is PointerMoveEvent) {
|
||||
onResponsiveUpdate?.call(event.position);
|
||||
}
|
||||
// If this gesture has a competing gesture (such as scrolling), and the
|
||||
// pointer has not moved far enough to get this panning accepted, a
|
||||
// pointer up event should still be considered as an accepted tap up.
|
||||
// Manually accept this gesture here, which triggers onDragEnd.
|
||||
// Sliding tap needs to handle 'up' events differently compared to typical
|
||||
// drag gestures. If there's another gesture recognizer (like scrolling)
|
||||
// competing and the pointer hasn't moved beyond the tolerance limit
|
||||
// (slop), this gesture must still be accepted.
|
||||
//
|
||||
// Simply calling `accept()` here to handle this won't work because it
|
||||
// would break backward compatibility with legacy buttons (see
|
||||
// https://github.com/flutter/flutter/issues/150980 for more details).
|
||||
// Legacy buttons recognize taps using `GestureDetector.onTap`, which
|
||||
// neither accepts nor rejects for short taps. Instead, they wait for the
|
||||
// default resolution as the last contender in the gesture arena.
|
||||
//
|
||||
// Therefore, this gesture should also follow the same strategy of not
|
||||
// immediately accepting or rejecting. This allows tap gestures to take
|
||||
// precedence for being inner, while sliding taps can take precedence over
|
||||
// scroll gestures when the latter give up.
|
||||
if (event is PointerUpEvent) {
|
||||
resolve(GestureDisposition.accepted);
|
||||
stopTrackingPointer(_primaryPointer!);
|
||||
onResponsiveEnd?.call(event.position);
|
||||
} else {
|
||||
super.handleEvent(event);
|
||||
_primaryPointer = null;
|
||||
// Do not call `super.handleEvent`, which gives up the pointer and thus
|
||||
// rejects the gesture.
|
||||
return;
|
||||
}
|
||||
if (event is PointerUpEvent || event is PointerCancelEvent) {
|
||||
if (event is PointerCancelEvent) {
|
||||
_primaryPointer = null;
|
||||
}
|
||||
}
|
||||
super.handleEvent(event);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -1155,6 +1155,52 @@ void main() {
|
||||
expect(pressed, null);
|
||||
});
|
||||
|
||||
testWidgets('Taps on legacy button calls onPressed and renders correctly', (WidgetTester tester) async {
|
||||
// Legacy buttons are implemented with [GestureDetector.onTap]. Apps that
|
||||
// use customized legacy buttons should continue to work.
|
||||
//
|
||||
// Regression test for https://github.com/flutter/flutter/issues/150980 .
|
||||
bool wasPressed = false;
|
||||
await tester.pumpWidget(
|
||||
createAppWithButtonThatLaunchesActionSheet(
|
||||
Builder(builder: (BuildContext context) {
|
||||
return CupertinoActionSheet(
|
||||
actions: <Widget>[
|
||||
LegacyAction(
|
||||
child: const Text('Legacy'),
|
||||
onPressed: () {
|
||||
expect(wasPressed, false);
|
||||
wasPressed = true;
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
CupertinoActionSheetAction(child: const Text('One'), onPressed: () {}),
|
||||
CupertinoActionSheetAction(child: const Text('Two'), onPressed: () {}),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Go'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(wasPressed, isFalse);
|
||||
|
||||
// Push the legacy button and hold for a while to activate the pressing effect.
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('Legacy')));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
expect(wasPressed, isFalse);
|
||||
await expectLater(
|
||||
find.byType(CupertinoActionSheet),
|
||||
matchesGoldenFile('cupertinoActionSheet.legacyButton.png'),
|
||||
);
|
||||
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(wasPressed, isTrue);
|
||||
expect(find.text('Legacy'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Action sheet width is correct when given infinite horizontal space', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
createAppWithButtonThatLaunchesActionSheet(
|
||||
@ -2054,3 +2100,32 @@ class OverrideMediaQuery extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Old-style action sheet buttons, which are implemented with
|
||||
// `GestureDetector.onTap`.
|
||||
class LegacyAction extends StatelessWidget {
|
||||
const LegacyAction({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final VoidCallback onPressed;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 57),
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 10.0),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user