mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 01:18:38 +08:00
This PR is the second in a series of refactors that aim to simplify event handling in Flame. The approach is as follows:
Added the MultiTapDispatcher component, which contains the logic that used to be within the HasTappableComponents mixin. This component is internal; it mounts to a FlameGame directly, and ensures that it is a singleton.
Whenever any TapCallbacks component is added to a game, it automatically adds the MultiTapDispatcher component (unless there is already one), which in turn registers a tap gesture detector with GestureDetectorBuilder and rebuilds the game widget.
The end result is that now in order to make a component tappable you only need to add the TapCallbacks mixin to that component, everything else will be handled by the framework.
Consequently, the HasTappableComponents mixin is now empty and marked as deprecated.
112 lines
2.7 KiB
Dart
112 lines
2.7 KiB
Dart
import 'package:flame/components.dart';
|
|
import 'package:flame/experimental.dart';
|
|
import 'package:flame/game.dart';
|
|
import 'package:flame/widgets.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class ComponentsNotifierExampleWidget extends StatefulWidget {
|
|
const ComponentsNotifierExampleWidget({super.key});
|
|
|
|
static String description = '''
|
|
Showcases how the components notifier can be used between
|
|
a flame game instance and widgets.
|
|
|
|
Tap the red dots to defeat the enemies and see the hud being updated
|
|
to reflect the current state of the game.
|
|
''';
|
|
|
|
@override
|
|
State<ComponentsNotifierExampleWidget> createState() =>
|
|
_ComponentsNotifierExampleWidgetState();
|
|
}
|
|
|
|
class _ComponentsNotifierExampleWidgetState
|
|
extends State<ComponentsNotifierExampleWidget> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
game = ComponentNotifierExample();
|
|
}
|
|
|
|
late final ComponentNotifierExample game;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
Positioned.fill(
|
|
child: GameWidget(game: game),
|
|
),
|
|
Positioned(
|
|
left: 16,
|
|
top: 16,
|
|
child: ComponentsNotifierBuilder<Enemy>(
|
|
notifier: game.componentsNotifier<Enemy>(),
|
|
builder: (context, notifier) {
|
|
return GameHud(
|
|
remainingEnemies: notifier.components.length,
|
|
onReplay: game.replay,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class GameHud extends StatelessWidget {
|
|
const GameHud({
|
|
super.key,
|
|
required this.remainingEnemies,
|
|
required this.onReplay,
|
|
});
|
|
|
|
final int remainingEnemies;
|
|
final VoidCallback onReplay;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: remainingEnemies == 0
|
|
? ElevatedButton(
|
|
onPressed: onReplay,
|
|
child: const Text('Play again'),
|
|
)
|
|
: Text('Remaining enemies: $remainingEnemies'),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class Enemy extends CircleComponent with TapCallbacks, Notifier {
|
|
Enemy({super.position})
|
|
: super(
|
|
radius: 20,
|
|
paint: Paint()..color = const Color(0xFFFF0000),
|
|
);
|
|
|
|
@override
|
|
void onTapUp(_) {
|
|
removeFromParent();
|
|
}
|
|
}
|
|
|
|
class ComponentNotifierExample extends FlameGame {
|
|
@override
|
|
Future<void> onLoad() async {
|
|
replay();
|
|
}
|
|
|
|
void replay() {
|
|
add(Enemy(position: Vector2(100, 100)));
|
|
add(Enemy(position: Vector2(200, 100)));
|
|
add(Enemy(position: Vector2(300, 100)));
|
|
}
|
|
}
|