Skip to content

Commit

Permalink
fix: Use ComponentKeys to keep track of dispatchers (#2629)
Browse files Browse the repository at this point in the history
This uses the `ComponentKey` API to register and unregister dispatchers,
so that we don't have to create unnecessary dispatcher components that
are removed directly in `onMount` if there already is a dispatcher or
the same type added.

This also opens up for registering components to the dispatchers so that
we don't have to go through the whole tree to deliver events which it
does currently.
  • Loading branch information
spydon authored Aug 7, 2023
1 parent c542d3c commit ff59aa1
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ mixin DoubleTapCallbacks on Component {
void onMount() {
super.onMount();
final game = findGame()! as FlameGame;
if (game.firstChild<DoubleTapDispatcher>() == null) {
game.add(DoubleTapDispatcher());
if (game.findByKey(const DoubleTapDispatcherKey()) == null) {
final dispatcher = DoubleTapDispatcher();
game.registerKey(const DoubleTapDispatcherKey(), dispatcher);
game.add(dispatcher);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ mixin DragCallbacks on Component {
void onMount() {
super.onMount();
final game = findGame()! as FlameGame;
if (game.firstChild<MultiDragDispatcher>() == null) {
game.add(MultiDragDispatcher());
if (game.findByKey(const MultiDragDispatcherKey()) == null) {
final dispatcher = MultiDragDispatcher();
game.registerKey(const MultiDragDispatcherKey(), dispatcher);
game.add(dispatcher);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ mixin TapCallbacks on Component {
void onMount() {
super.onMount();
final game = findGame()! as FlameGame;
if (game.firstChild<MultiTapDispatcher>() == null) {
game.add(MultiTapDispatcher());
if (game.findByKey(const MultiTapDispatcherKey()) == null) {
final dispatcher = MultiTapDispatcher();
game.registerKey(const MultiTapDispatcherKey(), dispatcher);
game.add(dispatcher);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ import 'package:flame/src/events/messages/double_tap_event.dart';
import 'package:flutter/gestures.dart';
import 'package:meta/meta.dart';

class DoubleTapDispatcherKey implements ComponentKey {
const DoubleTapDispatcherKey();

@override
int get hashCode => 20260645; // 'DoubleTapDispatcherKey' as hashCode

@override
bool operator ==(dynamic other) =>
other is DoubleTapDispatcherKey && other.hashCode == hashCode;
}

/// [DoubleTapDispatcher] propagates double-tap events to every components in
/// the component tree that is mixed with [DoubleTapCallbacks]. This will be
/// attached to the [FlameGame] instance automatically whenever any
/// [DoubleTapCallbacks] are mounted into the component tree.
@internal
class DoubleTapDispatcher extends Component with HasGameRef<FlameGame> {
final _components = <DoubleTapCallbacks>{};
bool _eventHandlerRegistered = false;

void _onDoubleTapDown(DoubleTapDownEvent event) {
event.deliverAtPoint(
Expand All @@ -37,30 +47,21 @@ class DoubleTapDispatcher extends Component with HasGameRef<FlameGame> {

@override
void onMount() {
if (game.firstChild<DoubleTapDispatcher>() == null) {
game.gestureDetectors.add(
DoubleTapGestureRecognizer.new,
(DoubleTapGestureRecognizer instance) {
instance.onDoubleTapDown =
(details) => _onDoubleTapDown(DoubleTapDownEvent(game, details));
instance.onDoubleTapCancel =
() => _onDoubleTapCancel(DoubleTapCancelEvent());
instance.onDoubleTap = () => _onDoubleTapUp(DoubleTapEvent());
},
);
_eventHandlerRegistered = true;
} else {
removeFromParent();
}
game.gestureDetectors.add(
DoubleTapGestureRecognizer.new,
(DoubleTapGestureRecognizer instance) {
instance.onDoubleTapDown =
(details) => _onDoubleTapDown(DoubleTapDownEvent(game, details));
instance.onDoubleTapCancel =
() => _onDoubleTapCancel(DoubleTapCancelEvent());
instance.onDoubleTap = () => _onDoubleTapUp(DoubleTapEvent());
},
);
}

@override
void onRemove() {
if (!_eventHandlerRegistered) {
return;
}

game.gestureDetectors.remove<DoubleTapGestureRecognizer>();
_eventHandlerRegistered = false;
game.unregisterKey(const DoubleTapDispatcherKey());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flame/src/components/core/component.dart';
import 'package:flame/src/components/core/component_key.dart';
import 'package:flame/src/components/mixins/draggable.dart';
import 'package:flame/src/events/component_mixins/drag_callbacks.dart';
import 'package:flame/src/events/flame_drag_adapter.dart';
Expand All @@ -14,6 +15,17 @@ import 'package:flame/src/game/game_render_box.dart';
import 'package:flutter/gestures.dart';
import 'package:meta/meta.dart';

class MultiDragDispatcherKey implements ComponentKey {
const MultiDragDispatcherKey();

@override
int get hashCode => 91604879; // 'MultiDragDispatcherKey' as hashCode

@override
bool operator ==(dynamic other) =>
other is MultiDragDispatcherKey && other.hashCode == hashCode;
}

/// **MultiDragDispatcher** facilitates dispatching of drag events to the
/// [DragCallbacks] components in the component tree. It will be attached to
/// the [FlameGame] instance automatically whenever any [DragCallbacks]
Expand All @@ -22,7 +34,6 @@ import 'package:meta/meta.dart';
class MultiDragDispatcher extends Component implements MultiDragListener {
/// The record of all components currently being touched.
final Set<TaggedComponent<DragCallbacks>> _records = {};
bool _eventHandlerRegistered = false;

FlameGame get game => parent! as FlameGame;

Expand Down Expand Up @@ -165,26 +176,18 @@ class MultiDragDispatcher extends Component implements MultiDragListener {

@override
void onMount() {
if (game.firstChild<MultiDragDispatcher>() == null) {
game.gestureDetectors.add<ImmediateMultiDragGestureRecognizer>(
ImmediateMultiDragGestureRecognizer.new,
(ImmediateMultiDragGestureRecognizer instance) {
instance.onStart = (Offset point) => FlameDragAdapter(this, point);
},
);
_eventHandlerRegistered = true;
} else {
// Ensures that only one MultiDragDispatcher is attached to the Game.
removeFromParent();
}
game.gestureDetectors.add<ImmediateMultiDragGestureRecognizer>(
ImmediateMultiDragGestureRecognizer.new,
(ImmediateMultiDragGestureRecognizer instance) {
instance.onStart = (Offset point) => FlameDragAdapter(this, point);
},
);
}

@override
void onRemove() {
if (_eventHandlerRegistered) {
game.gestureDetectors.remove<ImmediateMultiDragGestureRecognizer>();
_eventHandlerRegistered = false;
}
game.gestureDetectors.remove<ImmediateMultiDragGestureRecognizer>();
game.unregisterKey(const MultiDragDispatcherKey());
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,21 @@ import 'package:flame/src/game/game_render_box.dart';
import 'package:flutter/gestures.dart';
import 'package:meta/meta.dart';

class MultiTapDispatcherKey implements ComponentKey {
const MultiTapDispatcherKey();

@override
int get hashCode => 401913931; // 'MultiTapDispatcherKey' as hashCode

@override
bool operator ==(dynamic other) =>
other is MultiTapDispatcherKey && other.hashCode == hashCode;
}

@internal
class MultiTapDispatcher extends Component implements MultiTapListener {
/// The record of all components currently being touched.
final Set<TaggedComponent<TapCallbacks>> _record = {};
bool _eventHandlerRegistered = false;

FlameGame get game => parent! as FlameGame;

Expand Down Expand Up @@ -179,33 +189,25 @@ class MultiTapDispatcher extends Component implements MultiTapListener {

@override
void onMount() {
if (game.firstChild<MultiTapDispatcher>() == null) {
game.gestureDetectors.add<MultiTapGestureRecognizer>(
MultiTapGestureRecognizer.new,
(MultiTapGestureRecognizer instance) {
instance.longTapDelay = Duration(
milliseconds: (longTapDelay * 1000).toInt(),
);
instance.onTap = handleTap;
instance.onTapDown = handleTapDown;
instance.onTapUp = handleTapUp;
instance.onTapCancel = handleTapCancel;
instance.onLongTapDown = handleLongTapDown;
},
);
_eventHandlerRegistered = true;
} else {
// Ensures that only one MultiTapDispatcher is attached to the Game.
removeFromParent();
}
game.gestureDetectors.add<MultiTapGestureRecognizer>(
MultiTapGestureRecognizer.new,
(MultiTapGestureRecognizer instance) {
instance.longTapDelay = Duration(
milliseconds: (longTapDelay * 1000).toInt(),
);
instance.onTap = handleTap;
instance.onTapDown = handleTapDown;
instance.onTapUp = handleTapUp;
instance.onTapCancel = handleTapCancel;
instance.onLongTapDown = handleLongTapDown;
},
);
}

@override
void onRemove() {
if (_eventHandlerRegistered) {
game.gestureDetectors.remove<MultiTapGestureRecognizer>();
_eventHandlerRegistered = false;
}
game.gestureDetectors.remove<MultiTapGestureRecognizer>();
game.unregisterKey(const MultiTapDispatcherKey());
}

@override
Expand Down

0 comments on commit ff59aa1

Please sign in to comment.