Learn how to develop games in Flutter using the flame package:
- How to create a flutter app using
flame - How a
flamegame works - How to implement your first game with examples
Project setup
Create a normal Flutter project by running flutter create:
flutter create my_first_gameAdd flame in pubspec.yaml:
dependencies:
flutter:
sdk: flutter
flame: ^1.12.0Create a new assets folder in the root of the project for images, audio, fonts, and any other asset used in your project:
First example: Add player in the game
The entry point of every flame game is a class that extends FlameGame:
class MyGame extends FlameGame with SingleGameInstance {
@override
FutureOr<void> onLoad() {
super.onLoad();
}
@override
void update(double dt) {
super.update(dt);
}
}Every game in most engines relies on 2 methods:
onLoad: Called 1 time when the component is created to initialize state, load assets, and add components to the gameupdate: Called each frame to update the current state, move the player, react to events
In
flameevery element in the game is represented by a class that extends an instance ofComponent
We create a new Player class that extends SpriteComponent:
- Define the
sizeof the player in the constructor (size: Vector2.all(50.0)) - Implement the
onLoadfunction to initialize the player - Use
with HasGameRefto access a reference to the game (gameRef). UsinggameRefwe can load a sprite for the player (from theassets/imagesfolder) and place it at the center of the screen usingposition
import 'package:flame/components.dart';
class Player extends SpriteComponent with HasGameRef {
Player() : super(size: Vector2.all(50.0));
@override
Future<void> onLoad() async {
super.onLoad();
sprite = await gameRef.loadSprite('logo.png');
position = gameRef.size / 2;
}
}Remember to add logo.png inside assets/images and declare it inside pubspec.yaml:
flutter:
uses-material-design: true
assets:
- assets/images/logo.pngLet's create an instance of Player and add it to the game inside the onLoad method of FlameGame:
final Player _player = Player();
class MyGame extends FlameGame with SingleGameInstance {
@override
FutureOr<void> onLoad() {
add(_player);
}
}Finally, we add MyGame to our flutter app using GameWidget to render an instance of FlameGame like a normal flutter widget:
void main() {
runApp(
GameWidget(
game: MyGame(),
),
);
}This is it! Now lunch the game and you will see your player standing in the center of the screen.
There is more 🤩
Every week I dive headfirst into a topic, uncovering every hidden nook and shadow, to deliver you the most interesting insights
Not convinced? Well, let me tell you more about it
Anatomy of a game with flame
A game with flame starts with an entry class that extends FlameGame (or Game for low-level API control).
A game is a composition of
Component, similar to how a Flutter is built by composingWidget.
Like a Widget, every Component has its set of properties that define how to render it. For example every PositionComponent has position, size, anchor, scale, paint, and more.
You build the game by adding components using the add method:
class MyGame extends FlameGame {
@override
FutureOr<void> onLoad() {
add(PositionComponent(position: Vector2(10, 10)));
}
}You can either add a component directly (as in the example above), or you can create an instance of a component to add more properties to it:
class MyGame extends FlameGame {
@override
FutureOr<void> onLoad() {
add(MyComponent(100));
}
}
class MyComponent extends PositionComponent {
double health;
MyComponent(this.health);
}You then compose components just like widgets. You can override the onLoad method also inside MyComponent and add another component inside it:
class MyGame extends FlameGame {
@override
FutureOr<void> onLoad() {
add(MyComponent(100));
}
}
class MyComponent extends PositionComponent {
double health;
MyComponent(this.health);
@override
FutureOr<void> onLoad() {
add(PositionComponent(position: Vector2(10, 10)));
}
}Add controls using mixins
flame uses mixins to add controls to components:
class MyComponent extends PositionComponent with CollisionCallbacks {
double health;
MyComponent(this.health);
@override
void onCollisionStart(Set<Vector2> intersectionPoints, PositionComponent other) {
/// Detect collisions with other components in the game here ☝️
}
}By adding CollisionCallbacks our component now has access to a onCollisionStart method.
This method will be called every time our component collides with another component PositionComponent (other in the example).
💡 Tip: You can visit the Flame API and take a look at the Mixins sections to learn about new features to add to your components.
For example this is the Mixins API for collisions and this is the Mixins API for events 👈
Custom World and CameraComponent
A World is the entry component from where all other components originate.
A World is renderer using a CameraComponent to "look" at the game:
FlameGamehas oneWorld(calledworld) which is added by default and paired together with the defaultCameraComponent(calledcamera)
It is common practice to define our own instance of World and CameraComponent:
class CustomGame extends FlameGame {
CustomGame() : customWorld = CustomWorld() {
cameraComponent = CameraComponent(world: customWorld);
}
late final CameraComponent cameraComponent;
final CustomWorld customWorld;
@override
FutureOr<void> onLoad() async {
await super.onLoad();
addAll([cameraComponent, customWorld]);
}
}
class CustomWorld extends World with HasGameRef<CustomGame> {
CustomWorld();
}By doing this we now have control on the camera (we can define the viewport, move it around, attach it to the position of the player, and more).
We also control our own instance of World from where we can add all the components in the game.
Handling events
Some mixins must be added to the entry
FlameGameto enable certain functionalities.
Handling user events requires to add HasKeyboardHandlerComponents:
class CustomGame extends FlameGame with HasKeyboardHandlerComponents {
CustomGame() : customWorld = CustomWorld() {
cameraComponent = CameraComponent(world: customWorld);
}
late final CameraComponent cameraComponent;
final CustomWorld customWorld;
@override
FutureOr<void> onLoad() async {
await super.onLoad();
addAll([cameraComponent, customWorld]);
}
}We enabled listening to events for the components in our game.
We can now attach another mixin KeyboardHandler to a component to implement methods like onKeyEvent (for keyboard events):
class CustomWorld extends World with HasGameRef<CustomGame>, KeyboardHandler {
CustomWorld();
@override
bool onKeyEvent(RawKeyEvent event, Set<LogicalKeyboardKey> keysPressed) {
/// Handle keyboard events here 🎹
}
}There is more 🤩
Every week I dive headfirst into a topic, uncovering every hidden nook and shadow, to deliver you the most interesting insights
Not convinced? Well, let me tell you more about it
FlameGame in a flutter app
As we saw above, GameWidget allows to embed an instance of FlameGame like a normal widget everywhere in your flutter app.
This allows to implement normal flutter code and include a
flamegame everywhere as any other widget
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flame Game Jam 3.0',
theme: ThemeData(fontFamily: 'Font'),
home: const Scaffold(
body: GameView()
),
);
}
}
class GameView extends StatelessWidget {
const GameView({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: GameWidget(
game: MyGame(),
),
),
const Positioned(
bottom: 50,
left: 150,
right: 150,
child: Text("Some text at the bottom of the screen"),
),
],
);
}
}
class MyGame extends FlameGame {We can render a MaterialApp as usual. We then insert a GameWidget with an instance of MyGame inside the widget tree.
This allows to render normal widgets (Positioned, Text, Stack) on top of our flame game.
You can create the UI of your game as a normal flutter app using widgets, and use
flamewithGameWidgetto implement the game itself in the same app
State management
Like a normal Flutter app, you may need to share some state in your flame game as well (points, timer, equipment).
Since a
flamegame works like a normal flutter app, we can use the same state management strategies and packages used in any other app
The flame ecosystem has many Bridge Packages to integrate with state management solutions like riverpod (flame_riverpod) and bloc (flame_bloc).
For example, integrating with bloc requires to define the blocs like any usual (using MultiBlocProvider for example):
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flame Game Jam 3.0',
theme: ThemeData(fontFamily: 'Font'),
home: Scaffold(
body: MultiBlocProvider(
providers: [
BlocProvider(
create: (_) => GameStateCubit(),
),
],
child: const GameView(),
),
),
);
}
}We can then provide an instance of the bloc to FlameGame and use FlameMultiBlocProvider (from flame_bloc) to get access to the state:
class GameView extends StatelessWidget {
const GameView({super.key});
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: GameWidget(
game: MyGame(context.read<GameStateCubit>()),
),
),
],
);
}
}
class MyGame extends FlameGame {
MyGame(this.gameStateCubit);
final GameStateCubit gameStateCubit;
@override
FutureOr<void> onLoad() async {
await super.onLoad();
await add(
FlameMultiBlocProvider(
providers: [
FlameBlocProvider<GameStateCubit, GameState>.value(
value: gameStateCubit,
),
],
),
);
}
}Now any component in the game can use the FlameBlocReader mixin to access GameStateCubit:
class Player extends SpriteComponent
with FlameBlocReader<GameStateCubit, GameState> {
@override
Future<void> onLoad() {
bloc.state /// 👈 Access `bloc` from [FlameBlocReader]
}
}This is all you need to get started with flame in flutter:
- Create any component
- Compose components like widgets
- Add events and controls using mixin
- Define a custom
Worldand camera - Embed a
flamegame in a flutter app - State management
The flame ecosystem has also many solutions for other common requirements like audio, physics, tiles, animations, particles, and more.
You have everything you need to start working on your game!
If you are interested to learn more, every week I publish a new open source project and share notes and lessons learned in my newsletter. You can subscribe here below 👇
Thanks for reading.
