Flutter Tutorial

Flutter Intermediate Lesson 109 of 135 3 min read

Sprites and Sprite Animations in Flame: Images, Sprite Sheets and Assets

Use images in Flutter Flame games: assets in pubspec.yaml, SpriteComponent, sprite sheet animations, flipping, and idle, run and jump states.

On this page

Shapes are fine for learning, but real games use images. In Flame, an image drawn in the game is a sprite, and a sequence of images shown one after another is a sprite animation.

Images need files in your project, so the examples in this lesson do not run in the browser. Try them in a Flutter project on your computer.

1. Add the images

Flame looks for images in assets/images/:

my_game/
  assets/
    images/
      player.png
      player_run.png     ← a sprite sheet: 6 frames side by side
      coin.png
  lib/main.dart
  pubspec.yaml

Tell Flutter about the folder in pubspec.yaml:

flutter:
  assets:
    - assets/images/

Free game art: Kenney.nl and itch.io (check each licence).

2. A SpriteComponent

// Not standalone: needs assets/images/player.png
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart';

void main() => runApp(GameWidget(game: SpriteGame()));

class SpriteGame extends FlameGame {
  @override
  Future<void> onLoad() async {
    final sprite = await loadSprite('player.png'); // from assets/images/
    world.add(SpriteComponent(
      sprite: sprite,
      size: Vector2(64, 64),
      anchor: Anchor.center,
    ));
  }
}

loadSprite loads and caches the image. Loading always happens in onLoad, which is why it is async.

3. Animations from a sprite sheet

A sprite sheet is one image with several frames in a row. If player_run.png has 6 frames, each 32×32 pixels:

// Not standalone: needs assets/images/player_run.png
class Player extends SpriteAnimationComponent with HasGameReference<FlameGame> {
  Player() : super(size: Vector2.all(96), anchor: Anchor.center);

  @override
  Future<void> onLoad() async {
    animation = await game.loadSpriteAnimation(
      'player_run.png',
      SpriteAnimationData.sequenced(
        amount: 6,                     // number of frames
        stepTime: 0.1,                 // seconds per frame
        textureSize: Vector2.all(32),  // size of one frame in the image
      ),
    );
  }
}

The component plays the frames in a loop. Set loop: false in SpriteAnimationData.sequenced for one-time animations like explosions, and remove the component with animationTicker?.onComplete = removeFromParent.

4. Several animations: idle, run, jump

SpriteAnimationGroupComponent holds several animations and shows one at a time:

// Not standalone: needs the three sprite sheets
enum PlayerState { idle, run, jump }

class Player extends SpriteAnimationGroupComponent<PlayerState>
    with HasGameReference<FlameGame> {
  Player() : super(size: Vector2.all(96), anchor: Anchor.center);

  @override
  Future<void> onLoad() async {
    Future<SpriteAnimation> load(String file, int frames) => game.loadSpriteAnimation(
          file,
          SpriteAnimationData.sequenced(amount: frames, stepTime: 0.1, textureSize: Vector2.all(32)),
        );

    animations = {
      PlayerState.idle: await load('idle.png', 4),
      PlayerState.run: await load('run.png', 6),
      PlayerState.jump: await load('jump.png', 2),
    };
    current = PlayerState.idle;
  }

  void startRunning() => current = PlayerState.run;
}

5. Facing left and right

Flip the component instead of drawing a second set of images:

if (velocity.x < 0 && !isFlippedHorizontally) flipHorizontally();
if (velocity.x > 0 && isFlippedHorizontally) flipHorizontally();

Use anchor: Anchor.center so the sprite flips in place.

Pixel art looks blurry?

For crisp pixel art, draw sprites without smoothing: create them with Paint()..filterQuality = FilterQuality.none, or use a fixed-resolution camera and whole-number scaling (see Camera and world).

Try it yourself

Download a free character sprite sheet, add it to a project, and make the character run when you hold the right arrow key and stand still otherwise.

Practise in the playground Updated