Skip to content

Commit 84b77a0

Browse files
authored
refactor: Make GestureDetectorBuilder and detector/callback registering more resilient (#3900)
Make GestureDetectorBuilder and detector/callback registering more resilient wrt to multiple registrations (callbacks that use the same detector). This extracts a base class to dispatchers to share code and remove reundancies. It also makes the code on *Callbacks themselves trivial, and accounts for duplication. Before different callbacks could duplicate dispatchers. Now we properly guarantee dispatchers are singleton while allowing callbacks to require and use the same dispatcher. This is important for tertiary callbacks which will use the same underlying dispatcher as secondary.
1 parent 9f7c605 commit 84b77a0

22 files changed

Lines changed: 265 additions & 141 deletions

packages/flame/lib/src/events/component_mixins/double_tap_callbacks.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:flame/events.dart';
1010
/// At present, flutter detects only one double-tap events simultaneously.
1111
/// This means that if you're double-tapping two [DoubleTapCallbacks] located
1212
/// far away from each other, only one callback will be fired (or none).
13+
///
14+
/// This callback uses [DoubleTapDispatcher] to route events.
1315
mixin DoubleTapCallbacks on Component {
1416
/// This triggers when the pointer stops contacting the device after the
1517
/// second tap.
@@ -25,11 +27,6 @@ mixin DoubleTapCallbacks on Component {
2527
@override
2628
void onMount() {
2729
super.onMount();
28-
final game = findRootGame()!;
29-
if (game.findByKey(const DoubleTapDispatcherKey()) == null) {
30-
final dispatcher = DoubleTapDispatcher();
31-
game.registerKey(const DoubleTapDispatcherKey(), dispatcher);
32-
game.add(dispatcher);
33-
}
30+
DoubleTapDispatcher.addDispatcher(this);
3431
}
3532
}

packages/flame/lib/src/events/component_mixins/drag_callbacks.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:meta/meta.dart';
1010
/// the component.
1111
///
1212
/// This mixin is the replacement of the Draggable mixin.
13+
///
14+
/// This callback uses [MultiDragDispatcher] to route events.
1315
mixin DragCallbacks on Component {
1416
bool _isDragged = false;
1517

@@ -61,11 +63,6 @@ mixin DragCallbacks on Component {
6163
@mustCallSuper
6264
void onMount() {
6365
super.onMount();
64-
final game = findRootGame()!;
65-
if (game.findByKey(const MultiDragDispatcherKey()) == null) {
66-
final dispatcher = MultiDragDispatcher();
67-
game.registerKey(const MultiDragDispatcherKey(), dispatcher);
68-
game.add(dispatcher);
69-
}
66+
MultiDragDispatcher.addDispatcher(this);
7067
}
7168
}

packages/flame/lib/src/events/component_mixins/hover_callbacks.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import 'package:meta/meta.dart';
1111
/// component.
1212
///
1313
/// This mixin is the replacement of the Hoverable mixin.
14+
///
15+
/// This callback uses [PointerMoveDispatcher] to route events.
1416
mixin HoverCallbacks on Component implements PointerMoveCallbacks {
1517
bool _isHovered = false;
1618

@@ -56,6 +58,6 @@ mixin HoverCallbacks on Component implements PointerMoveCallbacks {
5658
@mustCallSuper
5759
void onMount() {
5860
super.onMount();
59-
PointerMoveCallbacks.onMountHandler(this);
61+
PointerMoveDispatcher.addDispatcher(this);
6062
}
6163
}

packages/flame/lib/src/events/component_mixins/long_press_callbacks.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import 'package:flutter/foundation.dart';
1919
/// long press.
2020
/// - [onLongPressEnd]: called when the pointer is lifted after a long press.
2121
/// - [onLongPressCancel]: called if the gesture is cancelled before completion.
22+
///
23+
/// This callback uses [LongPressDispatcher] to route events.
2224
mixin LongPressCallbacks on Component {
2325
bool _isLongPressing = false;
2426

@@ -46,11 +48,6 @@ mixin LongPressCallbacks on Component {
4648
@mustCallSuper
4749
void onMount() {
4850
super.onMount();
49-
final game = findRootGame()!;
50-
if (game.findByKey(const LongPressDispatcherKey()) == null) {
51-
final dispatcher = LongPressDispatcher();
52-
game.registerKey(const LongPressDispatcherKey(), dispatcher);
53-
game.add(dispatcher);
54-
}
51+
LongPressDispatcher.addDispatcher(this);
5552
}
5653
}

packages/flame/lib/src/events/component_mixins/pointer_move_callbacks.dart

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'package:meta/meta.dart';
44

55
/// This mixin can be added to a [Component] allowing it to receive
66
/// pointer movement events.
7+
///
8+
/// This callback uses [PointerMoveDispatcher] to route events.
79
mixin PointerMoveCallbacks on Component {
810
void onPointerMove(PointerMoveEvent event) {}
911

@@ -13,16 +15,6 @@ mixin PointerMoveCallbacks on Component {
1315
@mustCallSuper
1416
void onMount() {
1517
super.onMount();
16-
onMountHandler(this);
17-
}
18-
19-
static void onMountHandler(PointerMoveCallbacks instance) {
20-
final game = instance.findRootGame()!;
21-
const key = MouseMoveDispatcherKey();
22-
if (game.findByKey(key) == null) {
23-
final dispatcher = PointerMoveDispatcher();
24-
game.registerKey(key, dispatcher);
25-
game.add(dispatcher);
26-
}
18+
PointerMoveDispatcher.addDispatcher(this);
2719
}
2820
}

packages/flame/lib/src/events/component_mixins/scale_callbacks.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flame/events.dart';
33
import 'package:flame/src/events/flame_game_mixins/scale_dispatcher.dart';
44
import 'package:flutter/foundation.dart';
55

6+
/// This callback uses [ScaleDispatcher] to route events.
67
mixin ScaleCallbacks on Component {
78
bool _isScaling = false;
89

@@ -25,11 +26,6 @@ mixin ScaleCallbacks on Component {
2526
@mustCallSuper
2627
void onMount() {
2728
super.onMount();
28-
final game = findRootGame()!;
29-
if (game.findByKey(const ScaleDispatcherKey()) == null) {
30-
final dispatcher = ScaleDispatcher();
31-
game.registerKey(const ScaleDispatcherKey(), dispatcher);
32-
game.add(dispatcher);
33-
}
29+
ScaleDispatcher.addDispatcher(this);
3430
}
3531
}

packages/flame/lib/src/events/component_mixins/scroll_callbacks.dart

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,15 @@ import 'package:meta/meta.dart';
55

66
/// This mixin can be added to a [Component] allowing it to receive
77
/// pointer scroll (mouse wheel) events.
8+
///
9+
/// This callback uses [ScrollDispatcher] to route events.
810
mixin ScrollCallbacks on Component {
911
void onScroll(ScrollEvent event) {}
1012

1113
@override
1214
@mustCallSuper
1315
void onMount() {
1416
super.onMount();
15-
onMountHandler(this);
16-
}
17-
18-
static void onMountHandler(ScrollCallbacks instance) {
19-
final game = instance.findRootGame()!;
20-
const key = ScrollDispatcherKey();
21-
if (game.findByKey(key) == null) {
22-
final dispatcher = ScrollDispatcher();
23-
game.registerKey(key, dispatcher);
24-
game.add(dispatcher);
25-
}
17+
ScrollDispatcher.addDispatcher(this);
2618
}
2719
}

packages/flame/lib/src/events/component_mixins/secondary_tap_callbacks.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import 'package:meta/meta.dart';
1111
///
1212
/// Note that FlameGame _is_ a [Component] and does implement
1313
/// [containsLocalPoint]; so this can be used at the game level.
14+
///
15+
/// This callback uses [SecondaryTapDispatcher] to route events.
1416
mixin SecondaryTapCallbacks on Component {
1517
void onSecondaryTapDown(SecondaryTapDownEvent event) {}
1618
void onSecondaryTapUp(SecondaryTapUpEvent event) {}
@@ -20,11 +22,6 @@ mixin SecondaryTapCallbacks on Component {
2022
@mustCallSuper
2123
void onMount() {
2224
super.onMount();
23-
final game = findRootGame()!;
24-
if (game.findByKey(const SecondaryTapDispatcherKey()) == null) {
25-
final dispatcher = SecondaryTapDispatcher();
26-
game.registerKey(const SecondaryTapDispatcherKey(), dispatcher);
27-
game.add(dispatcher);
28-
}
25+
SecondaryTapDispatcher.addDispatcher(this);
2926
}
3027
}

packages/flame/lib/src/events/component_mixins/tap_callbacks.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:meta/meta.dart';
1010
///
1111
/// Note that FlameGame _is_ a [Component] and does implement
1212
/// [containsLocalPoint]; so this can be used at the game level.
13+
///
14+
/// This callback uses [MultiTapDispatcher] to route events.
1315
mixin TapCallbacks on Component {
1416
void onTapDown(TapDownEvent event) {}
1517
void onLongTapDown(TapDownEvent event) {}
@@ -20,11 +22,6 @@ mixin TapCallbacks on Component {
2022
@mustCallSuper
2123
void onMount() {
2224
super.onMount();
23-
final game = findRootGame()!;
24-
if (game.findByKey(const MultiTapDispatcherKey()) == null) {
25-
final dispatcher = MultiTapDispatcher();
26-
game.registerKey(const MultiTapDispatcherKey(), dispatcher);
27-
game.add(dispatcher);
28-
}
25+
MultiTapDispatcher.addDispatcher(this);
2926
}
3027
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import 'package:flame/components.dart';
2+
import 'package:flame/src/game/flame_game.dart';
3+
4+
abstract class Dispatcher<G extends FlameGame> extends Component
5+
with HasGameReference<G> {
6+
static void addDispatcher<T extends Component>(
7+
Component component,
8+
ComponentKey key,
9+
T Function() create,
10+
) {
11+
final game = component.findRootGame()!;
12+
if (game.findByKey(key) != null) {
13+
return;
14+
}
15+
final dispatcher = create();
16+
game.registerKey(key, dispatcher);
17+
game.add(dispatcher);
18+
}
19+
20+
static void removeDispatcher(FlameGame game, ComponentKey key) {
21+
game.unregisterKey(key);
22+
}
23+
}

0 commit comments

Comments
 (0)