Docs / Quick start - Flame

Quick start - Flame

Wire an Asobi backend into a Flame game. Flame games use flame_asobi - a thin binding that re-exports the Dart SDK plus Flame glue for input and state sync. No server yet? Run the server quickstart first.

1. Add the binding

Add flame_asobi - it pulls in the asobi Dart package transitively, so it's the only asobi dependency you declare.

dependencies:
  flame: ^1.22.0
  flame_asobi:
    git:
      url: https://github.com/widgrensit/flame_asobi.git
      ref: main

2. Hold one shared client

Auth and the WebSocket session live on a single AsobiClient reused across every screen and the game. Authenticate (guest) and queue for a match from your lobby, before entering the Flame game - see the Dart quickstart for the underlying connect / addToMatchmaker flow.

class GameConfig {
  static final client = AsobiClient('localhost', port: 8084);
  static const gameMode = 'default';
}

// in your lobby, before starting the game:
await GameConfig.client.auth.guest(deviceId, deviceSecret);
await GameConfig.client.realtime.connect();
GameConfig.client.realtime.addToMatchmaker(mode: GameConfig.gameMode);

3. Drive the game from asobi state

flame_asobi gives you two pieces. Input out: the HasAsobiInput mixin on your FlameGame turns Flame input into match.input each frame. State in: the AsobiNetworkSync component (added to world) bridges the match-state stream into the game loop and spawns/updates your components.

class ArenaGame extends FlameGame with HasAsobiInput {
  @override AsobiClient get inputClient => GameConfig.client;
  @override Map<String, dynamic>? buildMatchInput({...}) => {'move_x': dx, 'move_y': dy};

  @override
  Future<void> onLoad() async {
    world.add(AsobiNetworkSync(
      client: GameConfig.client,
      playerBuilder: (id, {required isLocal}) => ShipComponent(id, isLocal: isLocal),
      onStateUpdate: (state) => _hud.timeLeft = state.timeRemaining,
    ));
  }
}

Read snapshots through onStateUpdate and the sync's localPlayer; don't also subscribe to onMatchState yourself, or you double-process state.

Gotchas

  • One client, shared statically. Auth, the WS session, and playerId all live on that instance - reach it via inputClient => GameConfig.client, never a new client per screen.
  • Let the sync component own the stream. Lobby/UI code listens to raw .streams; inside Flame, AsobiNetworkSync subscribes and mutates on the Flame thread for you.
  • Cancel subscriptions. Cancel every StreamSubscription in dispose() / onRemove() across the lobby -> game -> results navigation.

What's next