Flutter Intermediate Lesson 104 of 135 3 min read
Your First Flame Game: FlameGame, GameWidget and a Moving Player
Build your first Flutter game with Flame: create a FlameGame, show it with GameWidget, and move a player every frame. Runs in your browser.
On this page
In this lesson you build a tiny game: a square that slides across the screen and wraps around when it leaves. It is only 30 lines, but it contains the three things every Flame game has: a game, a component and an update loop.
The complete game
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
void main() {
runApp(GameWidget(game: SlideGame()));
}
// 1. The game
class SlideGame extends FlameGame {
@override
Future<void> onLoad() async {
camera.viewfinder.anchor = Anchor.topLeft; // (0, 0) = top-left corner
world.add(Player());
}
@override
Color backgroundColor() => const Color(0xFF1B2A41);
}
// 2. A component
class Player extends RectangleComponent with HasGameReference<SlideGame> {
Player()
: super(
size: Vector2(60, 60),
position: Vector2(0, 200),
paint: Paint()..color = const Color(0xFF4FC3F7),
);
final double speed = 200; // pixels per second
// 3. Called every frame. dt = seconds since the last frame.
@override
void update(double dt) {
super.update(dt);
position.x += speed * dt;
if (position.x > game.size.x) {
position.x = -size.x; // wrap around to the left
}
}
}
Press Run. A blue square moves to the right and comes back from the left.
What each part does
GameWidget(game: SlideGame()) is a normal Flutter widget. You can put it anywhere: full screen with runApp, or inside a Scaffold, a Stack or a SizedBox.
SlideGame extends FlameGame is your game. It owns a world (where game objects live) and a camera (what you see). onLoad runs once, before the first frame: add your components here.
Player extends RectangleComponent is a component that draws a rectangle. It has a position, a size and a paint.
HasGameReference<SlideGame> gives the player a game property, so it can ask for game.size, the size of the screen.
update(double dt) is called about 60 times a second. dt (“delta time”) is the time since the previous frame, in seconds.
Why multiply by dt?
If you wrote position.x += 3 the square would move 3 pixels per frame. A fast phone at 120 frames per second would move it twice as fast as a slow one at 60. Multiplying by dt makes the speed pixels per second, the same on every device.
speed = 200 px/s
60 fps: dt ≈ 0.0167 → moves 3.3 px per frame → 200 px per second
120 fps: dt ≈ 0.0083 → moves 1.7 px per frame → 200 px per second
Putting the game inside an app
A game is just a widget, so you can mix it with Flutter UI:
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: GamePage()));
class GamePage extends StatelessWidget {
const GamePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('My game')),
body: GameWidget(game: FlameGame()),
);
}
}
Common mistakes
- Adding components in the constructor. Add them in
onLoad, when the game is ready and knows its size. - Forgetting
super.update(dt). Child components and effects stop updating. - Creating the game inside
build. Every rebuild would restart it. Create it once, in a field orinitState.
Try it yourself
- Make the square move down instead of right, wrapping at
game.size.y. - Add a second
Playerwith a different colour and speed. (Tip: give the constructorspeedandyparameters.)