From 51e8dc21f47a2588e6c551b32fcac17bd118e0a9 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Sun, 30 Aug 2026 14:45:52 -0400 Subject: [PATCH 1/3] chore: Kill PanDetector and the old event system --- doc/flame/collision_detection.md | 4 +- doc/flame/inputs/drag_events.md | 32 +--- doc/flame/inputs/gesture_input.md | 146 ------------------ doc/flame/inputs/inputs.md | 80 +++++++--- doc/flame/inputs/keyboard_input.md | 5 - doc/flame/inputs/other_inputs.md | 5 - doc/flame/inputs/pointer_events.md | 31 ++-- doc/flame/inputs/scale_events.md | 47 +++++- doc/flame/inputs/tap_events.md | 5 - doc/flame/migration.md | 86 ++++++++++- packages/flame/lib/events.dart | 10 -- packages/flame/lib/input.dart | 1 - packages/flame/lib/src/game/flame_game.dart | 6 - packages/flame/lib/src/game/game.dart | 2 +- .../game_widget/gesture_detector_builder.dart | 16 -- .../flame/lib/src/gestures/detectors.dart | 27 ---- packages/flame/lib/src/gestures/events.dart | 106 ------------- .../game_widget/game_widget_drag_test.dart | 38 ----- .../flame/test/gestures/detectors_test.dart | 102 ------------ 19 files changed, 213 insertions(+), 536 deletions(-) delete mode 100644 doc/flame/inputs/gesture_input.md delete mode 100644 packages/flame/lib/src/gestures/detectors.dart delete mode 100644 packages/flame/lib/src/gestures/events.dart delete mode 100644 packages/flame/test/game/game_widget/game_widget_drag_test.dart delete mode 100644 packages/flame/test/gestures/detectors_test.dart diff --git a/doc/flame/collision_detection.md b/doc/flame/collision_detection.md index 1645348b57f..08b3ceaff9d 100644 --- a/doc/flame/collision_detection.md +++ b/doc/flame/collision_detection.md @@ -10,7 +10,7 @@ other. For example, an arrow hitting an enemy or the player picking up a coin. In most collision detection systems you use something called hitboxes to create more precise bounding boxes of your components. In Flame the hitboxes are areas of the component that can react -to collisions and make [gesture input](inputs/gesture_input.md#gesturehitboxes) more accurate. +to collisions and make [gesture input](inputs/inputs.md#gesturehitboxes) more accurate. The collision detection system supports three different types of shapes that you can build hitboxes from, these shapes are Polygon, Rectangle and Circle. Multiple hitboxes can be added to a @@ -226,7 +226,7 @@ and two `RectangleHitbox`s as its hat. A hitbox can be used either for collision detection or for making gesture detection more accurate on top of components, see more regarding the latter in the section about the -[GestureHitboxes](inputs/gesture_input.md#gesturehitboxes) mixin. +[GestureHitboxes](inputs/inputs.md#gesturehitboxes) mixin. ### CollisionType diff --git a/doc/flame/inputs/drag_events.md b/doc/flame/inputs/drag_events.md index aed24edefc1..cad798aab2e 100644 --- a/doc/flame/inputs/drag_events.md +++ b/doc/flame/inputs/drag_events.md @@ -131,31 +131,7 @@ It can be used, for example, to change the component's visual appearance during ## Combining with ScaleCallbacks -A component can use both `DragCallbacks` and `ScaleCallbacks` at the same time. When both mixins are -present, single-finger gestures produce drag events and two-finger gestures produce both drag and -scale events. This is useful for components that should be draggable with one finger and -pinch-to-zoom or rotatable with two fingers. - -```dart -class InteractiveRectangle extends RectangleComponent - with ScaleCallbacks, DragCallbacks { - - double _initialAngle = 0; - - @override - void onDragUpdate(DragUpdateEvent event) { - position += event.localDelta; - } - - @override - void onScaleStart(ScaleStartEvent event) { - super.onScaleStart(event); - _initialAngle = angle; - } - - @override - void onScaleUpdate(ScaleUpdateEvent event) { - angle = _initialAngle + event.rotation; - } -} -``` +`DragCallbacks` and `ScaleCallbacks` can be used at the same time: single-finger gestures produce +drag events, and two-finger gestures produce both drag and scale events. See +[Combining with DragCallbacks](scale_events.md#combining-with-dragcallbacks) for how to make the two +work together, both on a component and for panning and zooming the camera. diff --git a/doc/flame/inputs/gesture_input.md b/doc/flame/inputs/gesture_input.md deleted file mode 100644 index 55feed89991..00000000000 --- a/doc/flame/inputs/gesture_input.md +++ /dev/null @@ -1,146 +0,0 @@ -# Gesture Input - -Gesture input in Flame is handled by the `Callbacks` mixins. They can be added to any `Component`, -and since `FlameGame` is itself a `Component`, adding one to your game class works exactly as well — -no wrapper component required. Each family has its own page: - -- [Tap Events](tap_events.md): `TapCallbacks`, `DoubleTapCallbacks`, and the secondary/tertiary - button variants -- [Drag Events](drag_events.md): `DragCallbacks` -- [Scale Events](scale_events.md): `ScaleCallbacks` -- [Long Press Events](long_press_events.md): `LongPressCallbacks` -- [Pointer Events](pointer_events.md): `MouseMoveCallbacks`, `HoverCallbacks`, `ScrollCallbacks` - -For other input documents, see also: - -- [Keyboard Input](keyboard_input.md): for keystrokes -- [Other Inputs](other_inputs.md): For joysticks, game pads, etc. - - -## PanDetector - -`PanDetector` is the last remaining detector mixin — the older style of input handling, added -directly to the game class instead of to a component. Everything else on that side has already been -replaced by the `Callbacks` mixins above. - -```{warning} -`PanDetector` will be removed. Prefer [`DragCallbacks`](drag_events.md), which -can be added to your `FlameGame` directly and additionally reports a -`pointerId` so that simultaneous drags can be told apart. -``` - -```text -- PanDetector - - onPanDown - - onPanStart - - onPanUpdate - - onPanEnd - - onPanCancel -``` - -Flame's GestureApi is provided by Flutter's Gesture Widgets, including -[GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), -[RawGestureDetector widget](https://api.flutter.dev/flutter/widgets/RawGestureDetector-class.html) -and [MouseRegion widget](https://api.flutter.dev/flutter/widgets/MouseRegion-class.html), you can -also read more about -[Flutter's gesture system](https://api.flutter.dev/flutter/gestures/gestures-library.html). - - -## Panning and zooming - -To handle panning and pinch-to-zoom at the same time, use the -[`DragCallbacks`](drag_events.md) and [`ScaleCallbacks`](scale_events.md) mixins together. Both are -driven by the same recognizer, so they can be combined freely: drag events are reported per pointer, -while scale events only start once two or more pointers are down. - -```dart -class MyGame extends FlameGame with DragCallbacks, ScaleCallbacks { - late double startZoom; - - void clampZoom() { - camera.viewfinder.zoom = camera.viewfinder.zoom.clamp(0.05, 3.0); - } - - @override - void onScaleStart(ScaleStartEvent event) { - super.onScaleStart(event); - startZoom = camera.viewfinder.zoom; - } - - @override - void onScaleUpdate(ScaleUpdateEvent event) { - camera.viewfinder.zoom = startZoom * event.verticalScale; - clampZoom(); - } - - @override - void onDragUpdate(DragUpdateEvent event) { - // Two-finger pinches emit both drag and scale; skip pan while zooming - if (isScaling) { - return; - } - final zoom = camera.viewfinder.zoom; - camera.moveBy((event.localDelta..negate()) / zoom); - } -} -``` - -This can also be seen in the -[zoom example](https://github.com/flame-engine/flame/blob/main/examples/lib/stories/camera_and_viewport/zoom_example.dart). - - -## Mouse cursor - -It is also possible to change the current mouse cursor displayed on the `GameWidget` region. To do -so the following code can be used inside the `Game` class - -```dart -mouseCursor.value = SystemMouseCursors.move; -``` - -To already initialize the `GameWidget` with a custom cursor, the `mouseCursor` property can be used - -```dart -GameWidget( - game: MouseCursorGame(), - mouseCursor: SystemMouseCursors.move, -); -``` - - -## Event coordinate system - -On events that have positions, like for example `Tap*` or `Drag`, you will notice that the -`eventPosition` attribute includes 2 fields: `global` and `widget`. Below you will find a brief -explanation about each of them. - - -### global - -The position where the event occurred considering the entire screen, same as -`globalPosition` in Flutter's native events. - - -### widget - -The position where the event occurred relative to the `GameWidget` position and size, same as -`localPosition` in Flutter's native events. - - -## Example - -```dart -class MyGame extends FlameGame with PanDetector { - // Other methods omitted - - @override - void onPanStart(DragStartInfo info) { - print('Player started panning on ${info.eventPosition.widget}'); - } - - @override - void onPanUpdate(DragUpdateInfo info) { - print('Player panned to ${info.eventPosition.widget}'); - } -} -``` diff --git a/doc/flame/inputs/inputs.md b/doc/flame/inputs/inputs.md index 2176243e037..d1f8e4b0be5 100644 --- a/doc/flame/inputs/inputs.md +++ b/doc/flame/inputs/inputs.md @@ -2,33 +2,70 @@ Games are interactive by nature, so handling player input is essential. Flame provides input handling that works on all platforms Flutter supports: touch on mobile, mouse and keyboard on -desktop, and -pointer events on the web. These APIs are designed as mixins that you add to your components, so -each component can independently decide which input events it cares about. This is similar to how +desktop, and pointer events on the web. These APIs are designed as mixins that you add to your +components, so each component can independently decide which input events it cares about. This is +similar to how Flutter's [GestureDetector](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html) works, but adapted for Flame's component tree. -- [Tap Events](tap_events.md) -- [Drag Events](drag_events.md) -- [Scale Events](scale_events.md) -- [Long Press Events](long_press_events.md) -- [Gesture Input](gesture_input.md) -- [Keyboard Input](keyboard_input.md) -- [Other Inputs and Helpers](other_inputs.md) -- [Pointer Events](pointer_events.md) +Since `FlameGame` is itself a `Component`, adding one of these mixins to your game class works +exactly as well as adding it to a component; no wrapper component required. + +- [Tap Events](tap_events.md): `TapCallbacks`, `SecondaryTapCallbacks`, `TertiaryTapCallbacks`, + `DoubleTapCallbacks` +- [Drag Events](drag_events.md): `DragCallbacks` +- [Scale Events](scale_events.md): `ScaleCallbacks` +- [Long Press Events](long_press_events.md): `LongPressCallbacks` +- [Pointer Events](pointer_events.md): `MouseMoveCallbacks`, `HoverCallbacks`, `ScrollCallbacks` +- [Keyboard Input](keyboard_input.md): for keystrokes - [Hardware Keyboard Detector](hardware_keyboard_detector.md) +- [Other Inputs and Helpers](other_inputs.md): for joysticks, game pads, etc. + +Under the hood, these are all built on Flutter's own gesture widgets, including the +[GestureDetector widget](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html), +[RawGestureDetector widget](https://api.flutter.dev/flutter/widgets/RawGestureDetector-class.html) +and [MouseRegion widget](https://api.flutter.dev/flutter/widgets/MouseRegion-class.html); you can +also read more about +[Flutter's gesture system](https://api.flutter.dev/flutter/gestures/gestures-library.html). + + +## Event coordinate system + +Every event that carries a position reports it in three coordinate systems: + +- `devicePosition`: relative to the entire screen, the same as `globalPosition` in Flutter's native + events. +- `canvasPosition`: relative to the `GameWidget` position and size, the same as `localPosition` in + Flutter's native events. This is Flame's "global" position. +- `localPosition`: relative to the component currently receiving the event, with the whole chain of + parent transforms (camera included) already applied. + +Events that represent a movement, such as `DragUpdateEvent`, additionally expose start and end +positions (`canvasStartPosition` / `canvasEndPosition`, and so on) plus the corresponding deltas: +`deviceDelta`, `canvasDelta` and `localDelta`. + +`localPosition` and `localDelta` are relative to whichever component is currently receiving the +event, so only read them inside the callback. Do not hold on to the event and read them afterwards: +once delivery is over they are no longer maintained, and depending on the event you will either get +a leftover value or an error. If you need the position later, copy it during the callback with +`event.localPosition.clone()`. + +When you mix a callback into your `FlameGame` directly, the game is that component; and since it has +no transform of its own, the local values there are equivalent to the canvas coordinates. ## GestureHitboxes -Every mixin whose events carry a position implements `PointerInputCallbacks` — that is all of the -above except keyboard — and they all decide whether an event belongs to a component by asking its -`containsLocalPoint()`, which for a `PositionComponent` is its rectangular bounds. The -`GestureHitboxes` mixin is used to recognize input on top of your `Component`s more accurately than -that. Say that you have a fairly round rock as a `SpriteComponent` for example, then you don't want -to register input that is in the corner of the image where the rock is not displayed. Then you can -use the `GestureHitboxes` mixin to define a more accurate circle or polygon (or another shape) for -which the input should be within for the event to be registered on your component. +Every mixin whose events carry a position implements `PointerInputCallbacks` (taps, drags, scales, +long presses and pointer events - but not the keyboard ones) and they all decide whether an event +belongs to a component by asking its `containsLocalPoint()`, which for a `PositionComponent` is its +rectangular bounds. + +The `GestureHitboxes` mixin is used to recognize input on top of your `Component`s more accurately. +Say that you have a round rock as a `SpriteComponent` for example, then you don't want to register +input that is in the corner of the image where the rock is not displayed; you can use the +`GestureHitboxes` mixin to define a more accurate boundary (circle, polygon, any shape) for the +event to check when propagating to your component. You can add new hitboxes to the component that has the `GestureHitboxes` mixin just like they are added in the `Collidable` example. @@ -46,9 +83,8 @@ Tap Events Drag Events Scale Events Long Press Events -Gesture Input -Keyboard Input -Other Inputs Pointer Events +Keyboard Input HardwareKeyboardDetector +Other Inputs ``` diff --git a/doc/flame/inputs/keyboard_input.md b/doc/flame/inputs/keyboard_input.md index ccf8e410f82..8845ff8442a 100644 --- a/doc/flame/inputs/keyboard_input.md +++ b/doc/flame/inputs/keyboard_input.md @@ -2,11 +2,6 @@ This includes documentation for keyboard inputs. -For other input documents, see also: - -- [Gesture Input](gesture_input.md): for mouse and touch pointer gestures -- [Other Inputs](other_inputs.md): For joysticks, game pads, etc. - ## Intro diff --git a/doc/flame/inputs/other_inputs.md b/doc/flame/inputs/other_inputs.md index 23738ea4c6a..73d8b697190 100644 --- a/doc/flame/inputs/other_inputs.md +++ b/doc/flame/inputs/other_inputs.md @@ -2,11 +2,6 @@ This includes documentation for input methods besides keyboard and mouse. -For other input documents, see also: - -- [Gesture Input](gesture_input.md): for mouse and touch pointer gestures -- [Keyboard Input](keyboard_input.md): for keystrokes - ## Joystick diff --git a/doc/flame/inputs/pointer_events.md b/doc/flame/inputs/pointer_events.md index 39b38110e48..94c4c6e199f 100644 --- a/doc/flame/inputs/pointer_events.md +++ b/doc/flame/inputs/pointer_events.md @@ -1,10 +1,5 @@ # Pointer Events -```{note} -This document describes the new events API. The old (legacy) approach, -which is still supported, is described in [](gesture_input.md). -``` - **Pointer events** are Flutter's generalized "mouse-movement"-type events (for desktop or web). If you want to interact with mouse movement events within your component or game, you can use the @@ -86,8 +81,7 @@ Play with the demo below to see the pointer hover events in action. ## ScrollCallbacks -If you want to handle mouse-wheel or trackpad scroll events at the component level, use the -`ScrollCallbacks` mixin. +If you want to handle mouse-wheel or trackpad scroll events, use the `ScrollCallbacks` mixin. ```dart class ScrollableSquare extends RectangleComponent with ScrollCallbacks { @@ -119,8 +113,8 @@ The `ScrollEvent` provides: Scroll events are delivered to **all** components under the pointer (not just the topmost one), unless set `continuePropagation = false` is set to stop it from bubbling further. -You can also mix `ScrollCallbacks` directly into your `FlameGame` to handle scrolling at the -game level. +You can also mix `ScrollCallbacks` directly into your `FlameGame` to handle scrolling anywhere on +the game surface. ### Scroll Demo @@ -130,3 +124,22 @@ game level. :page: scroll :show: widget code ``` + + +## Mouse cursor + +It is also possible to change the current mouse cursor displayed on the `GameWidget` region. To do +so the following code can be used inside the `Game` class + +```dart +mouseCursor.value = SystemMouseCursors.move; +``` + +To initialize the `GameWidget` with a custom cursor immediately, the `mouseCursor` property can be used: + +```dart +GameWidget( + game: MouseCursorGame(), + mouseCursor: SystemMouseCursors.move, +); +``` diff --git a/doc/flame/inputs/scale_events.md b/doc/flame/inputs/scale_events.md index aa841bcf1cc..d5d44d20f09 100644 --- a/doc/flame/inputs/scale_events.md +++ b/doc/flame/inputs/scale_events.md @@ -136,10 +136,10 @@ higher value requires a more deliberate gesture before scale events fire. ## Combining with DragCallbacks -A component can use both `ScaleCallbacks` and `DragCallbacks` at the same time. When both mixins are -present, single-finger gestures produce drag events and two-finger gestures produce both scale and -drag events. This is useful for components that should be draggable with one finger and -pinch-to-zoom or rotatable with two fingers. +`ScaleCallbacks` and `DragCallbacks` can be used at the same time. Both are driven by the same +recognizer, so they combine freely: single-finger gestures produce drag events, and two-finger +gestures produce both scale and drag events. This is useful for components that should be draggable +with one finger and pinch-to-zoom or rotatable with two fingers. ```dart class InteractiveRectangle extends RectangleComponent @@ -164,3 +164,42 @@ class InteractiveRectangle extends RectangleComponent } } ``` + +The same pair mixed into a `FlameGame` gives you the usual "drag to pan, pinch to zoom" camera +controls. Because a two-finger pinch emits drag events as well, the drag handler has to bail out +while a scale is in progress, otherwise the camera would pan and zoom at once: + +```dart +class MyGame extends FlameGame with DragCallbacks, ScaleCallbacks { + late double startZoom; + + void clampZoom() { + camera.viewfinder.zoom = camera.viewfinder.zoom.clamp(0.05, 3.0); + } + + @override + void onScaleStart(ScaleStartEvent event) { + super.onScaleStart(event); + startZoom = camera.viewfinder.zoom; + } + + @override + void onScaleUpdate(ScaleUpdateEvent event) { + camera.viewfinder.zoom = startZoom * event.verticalScale; + clampZoom(); + } + + @override + void onDragUpdate(DragUpdateEvent event) { + // Two-finger pinches emit both drag and scale; skip pan while zooming + if (isScaling) { + return; + } + final zoom = camera.viewfinder.zoom; + camera.moveBy((event.localDelta..negate()) / zoom); + } +} +``` + +This can also be seen in the +[zoom example](https://github.com/flame-engine/flame/blob/main/examples/lib/stories/camera_and_viewport/zoom_example.dart). diff --git a/doc/flame/inputs/tap_events.md b/doc/flame/inputs/tap_events.md index 26691c45aed..0d81566ceba 100644 --- a/doc/flame/inputs/tap_events.md +++ b/doc/flame/inputs/tap_events.md @@ -1,10 +1,5 @@ # Tap Events -```{note} -This document describes the new events API. The old (legacy) approach, -which is still supported, is described in [](gesture_input.md). -``` - **Tap events** are one of the most basic methods of interaction with a Flame game. These events occur when the user touches the screen with a finger, or clicks with a mouse, or taps with a stylus. A tap can be "long", but the finger isn't supposed to move during the gesture. Thus, touching the diff --git a/doc/flame/migration.md b/doc/flame/migration.md index 0ff31ac03b8..77405fe5851 100644 --- a/doc/flame/migration.md +++ b/doc/flame/migration.md @@ -366,9 +366,9 @@ class MyGame extends FlameGame with TapCallbacks { Flutter's "tap completed" callback on `MultiTapGestureRecognizer`. Use `onTapUp` instead, which fires at the same point in the gesture. -Because the new mixins are routed through `MultiDragScaleDispatcher`, they no longer conflict with -`PanDetector` in the gesture arena, and the assertion that used to guard against combining -`MultiTouchDragDetector` with `PanDetector` has been removed. +Because the new mixins are routed through `MultiDragScaleDispatcher`, the assertion that used to +guard against combining `MultiTouchDragDetector` with `PanDetector` in the gesture arena is gone +(as is `PanDetector` itself; see below). See [Tap Events](inputs/tap_events.md) and [Drag Events](inputs/drag_events.md) for the full replacement APIs. @@ -470,6 +470,86 @@ detector. See [Pointer Events](inputs/pointer_events.md) for the full replacement API. +### `PanDetector` removed, and with it the whole `*Info` event hierarchy + +`PanDetector` was the last of the game-level gesture detectors, so removing it also removes every +event class that existed to serve them: + +| Removed | Use instead | +| --- | --- | +| `PanDetector` | `DragCallbacks` | +| `DragStartInfo` | `DragStartEvent` | +| `DragUpdateInfo` | `DragUpdateEvent` | +| `DragEndInfo` | `DragEndEvent` | +| `DragDownInfo` | — | +| `TapDownInfo` | `TapDownEvent` | +| `TapUpInfo` | `TapUpEvent` | +| `PositionInfo` | `PositionEvent` | + +```dart +// Before +class MyGame extends FlameGame with PanDetector { + @override + void onPanStart(DragStartInfo info) { + player.startShooting(); + } + + @override + void onPanUpdate(DragUpdateInfo info) { + player.move(info.delta.global); + } + + @override + void onPanEnd(DragEndInfo info) { + player.stopShooting(); + } +} + +// After +class MyGame extends FlameGame with DragCallbacks { + @override + void onDragStart(DragStartEvent event) { + super.onDragStart(event); + player.startShooting(); + } + + @override + void onDragUpdate(DragUpdateEvent event) { + player.move(event.localDelta); + } + + @override + void onDragEnd(DragEndEvent event) { + super.onDragEnd(event); + player.stopShooting(); + } +} +``` + +A few differences to be aware of: + +- `onDragStart`, `onDragEnd` and `onDragCancel` are `@mustCallSuper`, because they maintain the + `isDragged` flag; your overrides have to call `super` first. +- There is no equivalent of `onPanDown`. Use `onDragStart`, which fires once the touch slop has been + exceeded, exactly like `onPanStart` did. +- The nested position and delta wrappers are gone: `info.eventPosition.widget` becomes + `event.canvasPosition`, and `info.delta.global` becomes `event.localDelta` (or `event.canvasDelta` + if you want the delta before any camera transform is applied). +- `DragEndEvent` exposes `velocity`, but there is no replacement for `DragEndInfo.primaryVelocity` — + it was permanently `null` anyway, since only axis-constrained recognizers ever set it. +- Every drag event carries a `pointerId`, so simultaneous drags can be told apart. `PanDetector` + could only ever track one. +- Like the other component callbacks, drags are routed by position: a component only receives a drag + that starts on top of it, as determined by `containsLocalPoint()`. Mixing `DragCallbacks` into + your `FlameGame` subclass directly, as above, keeps the old whole-surface behavior. + +With this, `package:flame/events.dart` and `package:flame/input.dart` no longer export any `*Detector` +mixin or `*Info` class, and `GestureDetectorBuilder.initializeGestures` - which existed only to wire +those detectors up — has been removed. + +See [Drag Events](inputs/drag_events.md) for the full replacement API. + + ### `onDragCancel` no longer delegates to `onDragEnd` `DragCallbacks.onDragCancel` used to convert the cancellation into an `onDragEnd` event by default, diff --git a/packages/flame/lib/events.dart b/packages/flame/lib/events.dart index 5ad9b802d0e..51de64184eb 100644 --- a/packages/flame/lib/events.dart +++ b/packages/flame/lib/events.dart @@ -76,13 +76,3 @@ export 'src/events/multi_drag_scale_recognizer.dart' show MultiDragScaleGestureRecognizer; export 'src/game/mixins/keyboard.dart' show HasKeyboardHandlerComponents, KeyboardEvents; -export 'src/gestures/detectors.dart' show PanDetector; -export 'src/gestures/events.dart' - show - DragDownInfo, - DragEndInfo, - DragStartInfo, - DragUpdateInfo, - PositionInfo, - TapDownInfo, - TapUpInfo; diff --git a/packages/flame/lib/input.dart b/packages/flame/lib/input.dart index 31b3809aaa0..7a2994c4f95 100644 --- a/packages/flame/lib/input.dart +++ b/packages/flame/lib/input.dart @@ -10,4 +10,3 @@ export 'src/components/input/sprite_button_component.dart'; export 'src/events/tap_config.dart'; export 'src/extensions/vector2.dart'; export 'src/game/mixins/keyboard.dart'; -export 'src/gestures/detectors.dart'; diff --git a/packages/flame/lib/src/game/flame_game.dart b/packages/flame/lib/src/game/flame_game.dart index 22f18af2eac..f8d026120e8 100644 --- a/packages/flame/lib/src/game/flame_game.dart +++ b/packages/flame/lib/src/game/flame_game.dart @@ -3,7 +3,6 @@ import 'dart:ui'; import 'package:flame/components.dart'; import 'package:flame/events.dart'; -import 'package:flame/input.dart'; import 'package:flame/src/components/core/component.dart'; import 'package:flame/src/devtools/dev_tools_service.dart'; import 'package:flame/src/effects/provider_interfaces.dart'; @@ -245,11 +244,6 @@ class FlameGame extends ComponentTreeRoot @override bool containsEventHandlerAt(Vector2 position) { - // Game-level detector mixins handle events for the entire game surface, - // so any in-bounds point is a hit. - if (this is PanDetector) { - return true; - } for (final component in super.componentsAtPoint(position)) { if (component is PointerInputCallbacks) { return true; diff --git a/packages/flame/lib/src/game/game.dart b/packages/flame/lib/src/game/game.dart index 1de828cf2e2..eb7401beaf6 100644 --- a/packages/flame/lib/src/game/game.dart +++ b/packages/flame/lib/src/game/game.dart @@ -36,7 +36,7 @@ abstract mixin class Game { /// functionality in Flutter. late final GestureDetectorBuilder gestureDetectors = GestureDetectorBuilder( refreshWidget, - )..initializeGestures(this); + ); /// Set by the MouseMoveDispatcher to receive mouse events from the /// game widget. diff --git a/packages/flame/lib/src/game/game_widget/gesture_detector_builder.dart b/packages/flame/lib/src/game/game_widget/gesture_detector_builder.dart index 1cda759442d..d431558bde5 100644 --- a/packages/flame/lib/src/game/game_widget/gesture_detector_builder.dart +++ b/packages/flame/lib/src/game/game_widget/gesture_detector_builder.dart @@ -1,4 +1,3 @@ -import 'package:flame/events.dart'; import 'package:flame/src/game/game.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; @@ -42,21 +41,6 @@ class GestureDetectorBuilder { child: child, ); } - - void initializeGestures(Game game) { - if (game is PanDetector) { - register( - PanGestureRecognizer.new, - (PanGestureRecognizer instance) { - instance.onDown = game.handlePanDown; - instance.onStart = game.handlePanStart; - instance.onUpdate = game.handlePanUpdate; - instance.onEnd = game.handlePanEnd; - instance.onCancel = game.onPanCancel; - }, - ); - } - } } bool hasMouseDetectors(Game game) { diff --git a/packages/flame/lib/src/gestures/detectors.dart b/packages/flame/lib/src/gestures/detectors.dart deleted file mode 100644 index 55c3bdd2d10..00000000000 --- a/packages/flame/lib/src/gestures/detectors.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flame/src/game/game.dart'; -import 'package:flame/src/gestures/events.dart'; -import 'package:flutter/gestures.dart'; - -mixin PanDetector on Game { - void onPanDown(DragDownInfo info) {} - void onPanStart(DragStartInfo info) {} - void onPanUpdate(DragUpdateInfo info) {} - void onPanEnd(DragEndInfo info) {} - void onPanCancel() {} - - void handlePanDown(DragDownDetails details) { - onPanDown(DragDownInfo.fromDetails(this, details)); - } - - void handlePanStart(DragStartDetails details) { - onPanStart(DragStartInfo.fromDetails(this, details)); - } - - void handlePanUpdate(DragUpdateDetails details) { - onPanUpdate(DragUpdateInfo.fromDetails(this, details)); - } - - void handlePanEnd(DragEndDetails details) { - onPanEnd(DragEndInfo.fromDetails(details)); - } -} diff --git a/packages/flame/lib/src/gestures/events.dart b/packages/flame/lib/src/gestures/events.dart deleted file mode 100644 index b4ce0149e2d..00000000000 --- a/packages/flame/lib/src/gestures/events.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'package:flame/extensions.dart'; -import 'package:flame/src/game/game.dart'; -import 'package:flutter/gestures.dart'; - -/// [EventPosition] converts position based events to three different coordinate -/// systems (global, local and game). -/// -/// global: coordinate system relative to the entire app; same as -/// `globalPosition` in Flutter. -/// widget: coordinate system relative to the GameWidget widget; same as -/// `localPosition` in Flutter. -class EventPosition { - final Game _game; - final Offset _globalPosition; - - /// Coordinates of the event relative to the whole screen - late final Vector2 global = _globalPosition.toVector2(); - - /// Coordinates of the event relative to the game widget position/size - late final Vector2 widget = _game.convertGlobalToLocalCoordinate(global); - - EventPosition(this._game, this._globalPosition); -} - -/// [EventDelta] converts deltas based events to two different values -/// (game and global). -/// -/// [global]: this is the raw value received by the event without any scale -/// applied to it; this is always the same as local because Flutter doesn't -/// apply any scaling. -class EventDelta { - final Offset _delta; - - /// Raw value relative to the game transformations - late final Vector2 global = _delta.toVector2(); - - EventDelta(this._delta); -} - -/// BaseInfo is the base class for Flame's input events. -/// This base class just wraps Flutter's [raw] attribute. -abstract class BaseInfo { - final T raw; - - BaseInfo(this.raw); -} - -/// A more specialized wrapper of Flame's base class for input events. -/// It adds the [eventPosition] field and is used by all position based -/// events on Flame. -abstract class PositionInfo extends BaseInfo { - final Game _game; - final Offset _globalPosition; - - late final eventPosition = EventPosition(_game, _globalPosition); - - PositionInfo( - this._game, - this._globalPosition, - T raw, - ) : super(raw); -} - -class TapDownInfo extends PositionInfo { - TapDownInfo.fromDetails( - Game game, - TapDownDetails raw, - ) : super(game, raw.globalPosition, raw); -} - -class TapUpInfo extends PositionInfo { - TapUpInfo.fromDetails( - Game game, - TapUpDetails raw, - ) : super(game, raw.globalPosition, raw); -} - -class DragDownInfo extends PositionInfo { - DragDownInfo.fromDetails( - Game game, - DragDownDetails raw, - ) : super(game, raw.globalPosition, raw); -} - -class DragStartInfo extends PositionInfo { - DragStartInfo.fromDetails( - Game game, - DragStartDetails raw, - ) : super(game, raw.globalPosition, raw); -} - -class DragUpdateInfo extends PositionInfo { - late final EventDelta delta = EventDelta(raw.delta); - - DragUpdateInfo.fromDetails( - Game game, - DragUpdateDetails raw, - ) : super(game, raw.globalPosition, raw); -} - -class DragEndInfo extends BaseInfo { - late final Vector2 velocity = raw.velocity.pixelsPerSecond.toVector2(); - double? get primaryVelocity => raw.primaryVelocity; - - DragEndInfo.fromDetails(super.raw); -} diff --git a/packages/flame/test/game/game_widget/game_widget_drag_test.dart b/packages/flame/test/game/game_widget/game_widget_drag_test.dart deleted file mode 100644 index 122e79eaae8..00000000000 --- a/packages/flame/test/game/game_widget/game_widget_drag_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flame/game.dart'; -import 'package:flame/input.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter_test/flutter_test.dart'; - -class _PanGame extends FlameGame with PanDetector { - bool panStarted = false; - bool panEnded = false; - - @override - void onPanStart(_) { - panStarted = true; - } - - @override - void onPanEnd(_) { - panEnded = true; - } -} - -void main() { - final panGame = FlameTester(_PanGame.new); - - group('GameWidget - PanDetector', () { - panGame.testGameWidget( - 'register drags', - verify: (game, tester) async { - await tester.drag( - find.byGame<_PanGame>(), - const Offset(50, 0), - ); - - expect(game.panStarted, isTrue); - expect(game.panEnded, isTrue); - }, - ); - }); -} diff --git a/packages/flame/test/gestures/detectors_test.dart b/packages/flame/test/gestures/detectors_test.dart deleted file mode 100644 index 432fcd26cdb..00000000000 --- a/packages/flame/test/gestures/detectors_test.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'package:flame/events.dart'; -import 'package:flame/game.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('PanDetector', () { - final panGame = FlameTester(_PanDetectorGame.new); - - panGame.testGameWidget( - 'can Register pan', - verify: (game, tester) async { - await tester.dragFrom(const Offset(10, 10), const Offset(20, 20)); - - expect(game.hasPanStart, isTrue); - expect(game.hasPanDown, isTrue); - expect(game.hasPanUpdate, isTrue); - expect(game.hasPanEnd, isTrue); - }, - ); - - testWithGame<_PanDetectorGame>( - 'can receive onPanDown', - _PanDetectorGame.new, - (game) async { - await game.ready(); - - game.handlePanDown(DragDownDetails()); - expect(game.hasPanDown, isTrue); - }, - ); - - testWithGame<_PanDetectorGame>( - 'can receive onPanEnd', - _PanDetectorGame.new, - (game) async { - await game.ready(); - - game.handlePanEnd(DragEndDetails()); - expect(game.hasPanEnd, isTrue); - }, - ); - - testWithGame<_PanDetectorGame>( - 'can receive onPanStart', - _PanDetectorGame.new, - (game) async { - await game.ready(); - - game.handlePanStart(DragStartDetails()); - expect(game.hasPanStart, isTrue); - }, - ); - - testWithGame<_PanDetectorGame>( - 'can receive onPanUpdate', - _PanDetectorGame.new, - (game) async { - await game.ready(); - - game.handlePanUpdate( - DragUpdateDetails(globalPosition: const Offset(10, 10)), - ); - expect(game.hasPanUpdate, isTrue); - }, - ); - }); -} - -class _PanDetectorGame extends FlameGame with PanDetector { - bool hasPanDown = false; - bool hasPanCancel = false; - bool hasPanEnd = false; - bool hasPanUpdate = false; - bool hasPanStart = false; - - @override - void onPanDown(DragDownInfo info) { - hasPanDown = true; - } - - @override - void onPanCancel() { - hasPanCancel = true; - } - - @override - void onPanEnd(DragEndInfo info) { - hasPanEnd = true; - } - - @override - void onPanUpdate(DragUpdateInfo info) { - hasPanUpdate = true; - } - - @override - void onPanStart(DragStartInfo info) { - hasPanStart = true; - } -} From c74feb3ff9869cee71e8558ccbe7f1b6290865dd Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Sun, 30 Aug 2026 14:58:29 -0400 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- doc/flame/inputs/scale_events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/flame/inputs/scale_events.md b/doc/flame/inputs/scale_events.md index d5d44d20f09..dd8c4f20ce5 100644 --- a/doc/flame/inputs/scale_events.md +++ b/doc/flame/inputs/scale_events.md @@ -185,7 +185,7 @@ class MyGame extends FlameGame with DragCallbacks, ScaleCallbacks { @override void onScaleUpdate(ScaleUpdateEvent event) { - camera.viewfinder.zoom = startZoom * event.verticalScale; + camera.viewfinder.zoom = startZoom * event.scale; clampZoom(); } From 2b5b16bac2341de6d47e98055be6e852310f16a0 Mon Sep 17 00:00:00 2001 From: Luan Nico Date: Sun, 30 Aug 2026 15:06:26 -0400 Subject: [PATCH 3/3] Align markdown table --- doc/flame/migration.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/flame/migration.md b/doc/flame/migration.md index 47c5dc11d8a..9962a0af25d 100644 --- a/doc/flame/migration.md +++ b/doc/flame/migration.md @@ -475,16 +475,16 @@ See [Pointer Events](inputs/pointer_events.md) for the full replacement API. `PanDetector` was the last of the game-level gesture detectors, so removing it also removes every event class that existed to serve them: -| Removed | Use instead | -| --- | --- | -| `PanDetector` | `DragCallbacks` | -| `DragStartInfo` | `DragStartEvent` | +| Removed | Use instead | +| ---------------- | ----------------- | +| `PanDetector` | `DragCallbacks` | +| `DragStartInfo` | `DragStartEvent` | | `DragUpdateInfo` | `DragUpdateEvent` | -| `DragEndInfo` | `DragEndEvent` | -| `DragDownInfo` | — | -| `TapDownInfo` | `TapDownEvent` | -| `TapUpInfo` | `TapUpEvent` | -| `PositionInfo` | `PositionEvent` | +| `DragEndInfo` | `DragEndEvent` | +| `DragDownInfo` | — | +| `TapDownInfo` | `TapDownEvent` | +| `TapUpInfo` | `TapUpEvent` | +| `PositionInfo` | `PositionEvent` | ```dart // Before