SaveSlotManager class

Manages multiple independent save slots (separate characters/profiles) on the same device — the classic "3 save files, pick a character on the home screen" casual/mid-core game pattern.

Deliberately a THIN coordinator, same posture as OnboardingCoordinatorService (IDEA-54) for a completely different domain: this only tracks slot metadata (id/display name/timestamps) and which slot is active, and hands out a namespaced keyFor a caller uses with their OWN VersionedJsonStore for the actual player-profile JSON. It never defines that schema itself — every game's profile shape is different, and this doesn't replace VersionedJsonStore (FEAT-05), it composes with it.

Inheritance
  • Object
  • GetLifeCycle
  • DisposableInterface
  • GetxService
  • SaveSlotManager

Constructors

SaveSlotManager({int? maxSlots})

Properties

activeSlotId → String?
The currently active slot id, or null if none has been set yet. Persists across restart. Reads straight through to StorageService rather than caching a copy here — it's a single plain key, not the burst-write-prone slot LIST, so there's no race to guard against and no reason to duplicate state StorageService already holds.
no setter
canCreateSlot → bool
true if createSlot can be called right now without throwing — always true when maxSlots is null (unlimited). A UI checks this to decide whether to disable a "Create slot" button, rather than re-deriving the same maxSlots == null || ... null-check itself.
no setter
debugPendingSaves → Future<void>
Awaits every save queued so far — lets a test deterministically wait for a burst of rapid calls to fully settle instead of guessing a delay.
no setter
hashCode → int
The hash code for this object.
no setterinherited
initialized → bool
Checks whether the controller has already been initialized.
no setterinherited
isClosed → bool
Checks whether the controller has already been closed.
no setterinherited
maxSlots → int?
ENH-68: caps how many slots createSlot will allow — null (default) means unlimited, unchanged from before this existed. Deliberately not defaulted to a specific number (3? 5?): how many save slots a game offers is a product decision for the consumer app, not this package.
final
onDelete → InternalFinalCallback<void>
Internal callback that starts the cycle of this controller.
finalinherited
onStart → InternalFinalCallback<void>
Called at the exact moment the widget is allocated in memory. It uses an internal "callable" type, to avoid any @overrides in subclases. This method should be internal and is required to define the lifetime cycle of the subclass.
finalinherited
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

$configureLifeCycle() → void
inherited
createSlot(String displayName) → SaveSlotMeta
Creates a new slot with a freshly-generated, guaranteed-unique id (even across a burst of rapid calls in the same millisecond). createdAtMs/lastPlayedAtMs both start at nowMsClamped() — the same anti-clock-rewind clock every time-gated system in this package uses, never raw DateTime.now().
deleteSlot(String id) → Future<void>
Deletes slot id: removes its metadata AND every key namespaced to it via keyFor (through StorageService.removeAllWithPrefix) — no leftover keys from a deleted slot. If id was the activeSlotId, that becomes null afterward (never left pointing at a slot that no longer exists). Throws ArgumentError if id doesn't exist.
keyFor(String slotId, String suffix) → String
A key namespaced to slotId for suffix — hand this to a VersionedJsonStore's own key: for that slot's player-profile data. Stable/deterministic (same inputs always produce the same key). Every slotId is always one this class generated itself via _generateUniqueId (slot_<digits>, never containing an underscore-delimited component that could extend another id's own prefix — see that method's own doc for why that property matters for deleteSlot's removeAllWithPrefix call), so 2 different slotId/suffix pairs can never collide.
listSlots() → List<SaveSlotMeta>
Every slot, most-recently-played first — the classic "Continue" UX.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onClose() → void
Called before onDelete method. onClose might be used to dispose resources used by the controller. Like closing events, or streams before the controller is destroyed. Or dispose objects that can potentially create some memory leaks, like TextEditingControllers, AnimationControllers. Might be useful as well to persist some data on disk.
inherited
onInit() → void
Called immediately after the widget is allocated in memory. You might use this to initialize something for the controller.
inherited
onReady() → void
Called 1 frame after onInit(). It is the perfect place to enter navigation events, like snackbar, dialogs, or a new route, or async request.
inherited
renameSlot(String id, String newDisplayName) → void
Renames slot id. Throws ArgumentError if id doesn't exist.
restoreSlotMeta(SaveSlotMeta meta) → void
Registers/updates meta directly in the slot list — BUG-41: used by disaster-recovery restore (DisasterRecoverySaveExport.applyRestore) right after it restores a slot's actual data via StorageService.importWithPrefix, which only writes the slot's namespaced keys and never touches this manager's own separate _metaStorageKey list — without this, the restored data exists on disk but listSlots never surfaces it (an orphaned slot).
setActiveSlot(String id) → Future<void>
Marks id as the active slot. Throws ArgumentError (without changing activeSlotId) if id doesn't exist — never actives into a slot that was never created (or was already deleted).
toString() → String
A string representation of this object.
inherited
touchSlot(String id) → void
Bumps slot id's lastPlayedAtMs to now — call when a play session in that slot starts, so listSlots surfaces it first next time. Throws ArgumentError if id doesn't exist.

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Static Properties

maybe → SaveSlotManager?
Gets the instance if already registered (safe to call from game/widget tests).
no setter