diff --git a/packages/flutter/lib/src/cupertino/button.dart b/packages/flutter/lib/src/cupertino/button.dart index 25d7018e31..980ff4e578 100644 --- a/packages/flutter/lib/src/cupertino/button.dart +++ b/packages/flutter/lib/src/cupertino/button.dart @@ -356,8 +356,10 @@ class _CupertinoButtonState extends State with SingleTickerProv } bool _buttonHeldDown = false; + bool _tapInProgress = false; void _handleTapDown(TapDownDetails event) { + _tapInProgress = true; if (!_buttonHeldDown) { _buttonHeldDown = true; _animate(); @@ -365,6 +367,7 @@ class _CupertinoButtonState extends State with SingleTickerProv } void _handleTapUp(TapUpDetails event) { + _tapInProgress = false; if (_buttonHeldDown) { _buttonHeldDown = false; _animate(); @@ -377,19 +380,20 @@ class _CupertinoButtonState extends State with SingleTickerProv } void _handleTapCancel() { + _tapInProgress = false; if (_buttonHeldDown) { _buttonHeldDown = false; _animate(); } } - void _handTapMove(TapMoveDetails event) { + void _handleTapMove(TapMoveDetails event) { final RenderBox renderObject = context.findRenderObject()! as RenderBox; final Offset localPosition = renderObject.globalToLocal(event.globalPosition); final bool buttonShouldHeldDown = renderObject.paintBounds .inflate(CupertinoButton.tapMoveSlop()) .contains(localPosition); - if (buttonShouldHeldDown != _buttonHeldDown) { + if (_tapInProgress && buttonShouldHeldDown != _buttonHeldDown) { _buttonHeldDown = buttonShouldHeldDown; _animate(); } @@ -512,7 +516,7 @@ class _CupertinoButtonState extends State with SingleTickerProv instance.onTapDown = enabled ? _handleTapDown : null; instance.onTapUp = enabled ? _handleTapUp : null; instance.onTapCancel = enabled ? _handleTapCancel : null; - instance.onTapMove = enabled ? _handTapMove : null; + instance.onTapMove = enabled ? _handleTapMove : null; instance.gestureSettings = gestureSettings; }, ), diff --git a/packages/flutter/test/cupertino/button_test.dart b/packages/flutter/test/cupertino/button_test.dart index 95990f73dc..c7ea37d35a 100644 --- a/packages/flutter/test/cupertino/button_test.dart +++ b/packages/flutter/test/cupertino/button_test.dart @@ -912,6 +912,30 @@ void main() { expect(opacity.opacity.value, 0.4); }, variant: TargetPlatformVariant.all()); + testWidgets('Drag outside button within ListView does not leave the button pressed', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + boilerplate( + child: ListView( + children: [CupertinoButton(onPressed: () {}, child: const Text('Tap me'))], + ), + ), + ); + final FadeTransition opacity = tester.widget( + find.descendant(of: find.byType(CupertinoButton), matching: find.byType(FadeTransition)), + ); + + final TestGesture gesture = await tester.createGesture(); + addTearDown(gesture.removePointer); + + await gesture.down(tester.getTopLeft(find.byType(CupertinoButton))); + await gesture.moveBy(const Offset(1, 1)); + await gesture.moveBy(Offset(0, -CupertinoButton.tapMoveSlop() - 5)); + await tester.pumpAndSettle(); + expect(opacity.opacity.value, 1.0); + }); + testWidgets('onPressed trigger takes into account MoveSlop.', (WidgetTester tester) async { bool value = false; await tester.pumpWidget(