Flutter Tutorial

Flutter Intermediate Lesson 117 of 135 2 min read

Save the High Score and Game Settings in Flame with shared_preferences

Save a high score, settings and progress in a Flutter Flame game with shared_preferences, with a runnable game that remembers your best score.

On this page

Players want to beat their best score, and they expect settings (sound on or off, unlocked levels) to be remembered. For small amounts of data like this, shared_preferences is perfect: it saves simple values on the device (and in the browser’s storage on the web).

Tap fast: a game that remembers your record

You have 5 seconds to tap the circle as often as you can. The best score is saved, so it is still there after you reload the page.

import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final prefs = await SharedPreferences.getInstance();
  runApp(GameWidget(game: RecordGame(prefs)));
}

class RecordGame extends FlameGame {
  RecordGame(this.prefs);
  final SharedPreferences prefs;

  int taps = 0;
  int best = 0;
  double timeLeft = 0;
  bool playing = false;
  late final TextComponent info;

  @override
  Future<void> onLoad() async {
    camera.viewfinder.anchor = Anchor.topLeft;
    best = prefs.getInt('best') ?? 0; // load the saved record
    info = TextComponent(position: Vector2(16, 16));
    world.add(info);
    world.add(TapButton()..position = size / 2);
    showInfo();
  }

  void tapped() {
    if (!playing) {
      playing = true;
      taps = 0;
      timeLeft = 5;
    }
    taps++;
  }

  @override
  void update(double dt) {
    super.update(dt);
    if (!playing) return;
    timeLeft -= dt;
    if (timeLeft <= 0) {
      playing = false;
      if (taps > best) {
        best = taps;
        prefs.setInt('best', best); // save the new record
      }
    }
    showInfo();
  }

  void showInfo() {
    info.text = playing
        ? 'Taps: $taps   Time: ${timeLeft.toStringAsFixed(1)}'
        : 'Tap the circle to start!   Best: $best';
  }
}

class TapButton extends CircleComponent with TapCallbacks, HasGameReference<RecordGame> {
  TapButton() : super(radius: 60, anchor: Anchor.center, paint: Paint()..color = Colors.teal);

  @override
  void onTapDown(TapDownEvent event) {
    game.tapped();
    scale = Vector2.all(0.9);
  }

  @override
  void onTapUp(TapUpEvent event) => scale = Vector2.all(1);
}

Reading and writing

final prefs = await SharedPreferences.getInstance();

await prefs.setInt('best', 42);
await prefs.setBool('soundOn', false);
await prefs.setStringList('unlocked', ['level1', 'level2']);

final best = prefs.getInt('best') ?? 0;        // 0 if nothing saved yet
final soundOn = prefs.getBool('soundOn') ?? true;

Load preferences once, before runApp or in onLoad, and pass them to the game. Save straight away when something changes.

What to save where

DataTool
High score, settings, unlocked levelsshared_preferences
A full save game (inventory, map state)JSON in a file, or SQLite
Online leaderboardsFirebase or Google Play Games / Game Center

More about the package in shared_preferences.

Try it yourself

Save the last five scores as a string list and show them in a column under the circle.

Practise in the playground Updated