diff --git a/packages/flame/lib/src/events/flame_game_mixins/multi_tap_dispatcher.dart b/packages/flame/lib/src/events/flame_game_mixins/multi_tap_dispatcher.dart index 025f340c8b2..64a05453723 100644 --- a/packages/flame/lib/src/events/flame_game_mixins/multi_tap_dispatcher.dart +++ b/packages/flame/lib/src/events/flame_game_mixins/multi_tap_dispatcher.dart @@ -87,6 +87,8 @@ class MultiTapDispatcher extends Component implements MultiTapListener { }, deliverToAll: true, ); + + _tapCancelImpl(event.toTapCancel()); } /// Called when there was an [onTapDown] event previously, but the [onTapUp] diff --git a/packages/flame/lib/src/events/messages/tap_up_event.dart b/packages/flame/lib/src/events/messages/tap_up_event.dart index c38e79703f7..bf176efdc24 100644 --- a/packages/flame/lib/src/events/messages/tap_up_event.dart +++ b/packages/flame/lib/src/events/messages/tap_up_event.dart @@ -1,5 +1,6 @@ import 'package:flame/extensions.dart'; import 'package:flame/src/events/messages/position_event.dart'; +import 'package:flame/src/events/messages/tap_cancel_event.dart'; import 'package:flame/src/events/messages/tap_down_event.dart'; import 'package:flutter/gestures.dart'; @@ -23,6 +24,8 @@ class TapUpEvent extends PositionEvent { final PointerDeviceKind deviceKind; + TapCancelEvent toTapCancel() => TapCancelEvent(pointerId); + @override String toString() => 'TapUpEvent(canvasPosition: $canvasPosition, ' diff --git a/packages/flame/test/events/component_mixins/tap_callbacks_test.dart b/packages/flame/test/events/component_mixins/tap_callbacks_test.dart index 3c8a7b31afe..3047a458c5f 100644 --- a/packages/flame/test/events/component_mixins/tap_callbacks_test.dart +++ b/packages/flame/test/events/component_mixins/tap_callbacks_test.dart @@ -153,6 +153,63 @@ void main() { }, ); + testWidgets( + 'tap that starts inside the component and ends outside is cancelled', + (tester) async { + final component = _TapCallbacksComponent() + ..x = 10 + ..y = 10 + ..width = 10 + ..height = 10; + final game = FlameGame(children: [component]); + await tester.pumpWidget(GameWidget(game: game)); + await tester.pump(); + await tester.pump(); + expect(component.isMounted, isTrue); + + final gesture = await tester.startGesture(const Offset(10, 10)); + await tester.pump(const Duration(milliseconds: 500)); + await gesture.moveTo(const Offset(10, 9)); + await tester.pump(const Duration(milliseconds: 500)); + await gesture.up(); + + await tester.pump(); + + expect(component.tapDownEvent, equals(1)); + expect(component.tapUpEvent, equals(0)); + expect(component.tapCancelEvent, equals(1)); + }, + ); + + testWidgets( + 'tap that starts and ends in different positions' + ' inside the component is handled', + (tester) async { + final component = _TapCallbacksComponent() + ..x = 10 + ..y = 10 + ..width = 10 + ..height = 10; + final game = FlameGame(children: [component]); + await tester.pumpWidget(GameWidget(game: game)); + await tester.pump(); + await tester.pump(); + expect(component.isMounted, isTrue); + + final gesture = await tester.startGesture(const Offset(10, 10)); + await tester.pump(const Duration(milliseconds: 500)); + await gesture.moveTo(const Offset(10, 11)); + await tester.pump(const Duration(milliseconds: 500)); + await gesture.up(); + + await tester.pump(); + + expect(component.tapDownEvent, equals(1)); + expect(component.tapUpEvent, equals(1)); + expect(component.tapCancelEvent, equals(0)); + }, + ); + testWithGame( 'make sure the FlameGame can registers TapCallback on itself', _TapCallbacksGame.new,