Flutter Tutorial

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 or initState.

Try it yourself

  1. Make the square move down instead of right, wrapping at game.size.y.
  2. Add a second Player with a different colour and speed. (Tip: give the constructor speed and y parameters.)
Practise in the playground Updated