Learn how to develop games in Flutter using the flame
package:
- How to create a flutter app using
flame
- How a
flame
game works - How to implement your first game with examples
Project setup
Create a normal Flutter project by running flutter create
:
flutter create my_first_game
Add flame
in pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
flame: ^1.12.0
Create a new assets
folder in the root of the project for images, audio, fonts, and any other asset used in your project:
flame uses the assets folder to load assets for the game
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
flame
every 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
size
of the player in the constructor (size: Vector2.all(50.0)
) - Implement the
onLoad
function to initialize the player - Use
with HasGameRef
to access a reference to the game (gameRef
). UsinggameRef
we can load a sprite for the player (from theassets/images
folder) 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
:
All game assets must be included inside the assets folder
flutter:
uses-material-design: true
assets:
- assets/images/logo.png
Let'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 🤩
Timeless coding principles, practices, and tools that make a difference, regardless of your language or framework, delivered in your inbox every week.
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:
FlameGame
has 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
FlameGame
to 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 🤩
Timeless coding principles, practices, and tools that make a difference, regardless of your language or framework, delivered in your inbox every week.
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
flame
game 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
flame
withGameWidget
to 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
flame
game 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
World
and camera - Embed a
flame
game 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.