Files
flame/examples/lib/stories/components/components_notifier_provider_example.dart
Erick bd7f51f5b6 feat: Adding ComponentNotifier API (#1889)
This adds a proposal for a new API for flame, the ComponentNotifier.

This API offers the user change notifiers classes that are tied to FlameGame and its components so the user can be notified when a component is added, removed or updated.

This will enable users to:

    Take the benefit of reactive programming inside the game
    Have a simple way of watching certain states from the game, on Flutter Widgets

One important note here is that this proposal does not mean to replace integrations like flame_bloc, but rather provider an simple and out of the box solution, without any need of additional packages, since change notifiers are provided by flutter itself.

Opening this as draft for now to get feedback on the implementation, will write tests and docs once we have the final implementation.
2022-11-26 13:44:12 +00:00

106 lines
2.6 KiB
Dart

import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ComponentsNotifierProviderExampleWidget extends StatefulWidget {
const ComponentsNotifierProviderExampleWidget({super.key});
static String description = '''
Similar to the Components Notifier example, but uses provider
instead of the built in ComponentsNotifierBuilder widget.
''';
@override
State<ComponentsNotifierProviderExampleWidget> createState() =>
_ComponentsNotifierProviderExampleWidgetState();
}
class _ComponentsNotifierProviderExampleWidgetState
extends State<ComponentsNotifierProviderExampleWidget> {
@override
void initState() {
super.initState();
game = ComponentNotifierExample();
}
late final ComponentNotifierExample game;
@override
Widget build(BuildContext context) {
return Scaffold(
body: MultiProvider(
providers: [
Provider<ComponentNotifierExample>.value(value: game),
ChangeNotifierProvider<ComponentsNotifier<Enemy>>(
create: (_) => game.componentsNotifier<Enemy>(),
),
],
child: Stack(
children: [
Positioned.fill(
child: GameWidget(game: game),
),
const Positioned(
left: 16,
top: 16,
child: GameHud(),
),
],
),
),
);
}
}
class GameHud extends StatelessWidget {
const GameHud({super.key});
@override
Widget build(BuildContext context) {
final enemies = context.watch<ComponentsNotifier<Enemy>>().components;
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: enemies.isEmpty
? ElevatedButton(
child: const Text('Play again'),
onPressed: () {
context.read<ComponentNotifierExample>().replay();
},
)
: Text('Remaining enemies: ${enemies.length}'),
),
);
}
}
class Enemy extends CircleComponent with Tappable, Notifier {
Enemy({super.position})
: super(
radius: 20,
paint: Paint()..color = const Color(0xFFFF0000),
);
@override
bool onTapUp(_) {
removeFromParent();
return true;
}
}
class ComponentNotifierExample extends FlameGame with HasTappables {
@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)));
}
}