Persistence
PoseFlow Studio doesn’t know about Firebase, SQLite, or any specific storage backend. It talks to a small set of port interfaces; you implement them against whatever your host already uses.
This page is the wiring contract.
The StudioPersistence bundle
class StudioPersistence { final StudioCatalog catalog; final StudioAudit audit; final StudioPhaseRepository phases; final StudioVersionRepository versions;}Four small interfaces. The bundle wraps them so you can pass a
single value through StudioScope.
StudioCatalog
The main read/write port for movements.
abstract class StudioCatalog { Future<List<MovementSummary>> listSummaries({String? domainFilter}); Future<Movement?> load(String id); Future<void> save( Movement movement, { required String domain, String status, }); String export(Movement movement); // → JSON string}The Studio calls save() on every commit and load() when the
editor opens with a movement id. listSummaries() powers the
“Open” picker dialog.
StudioAudit
Append-only event log. Optional but recommended.
abstract class StudioAudit { Future<void> log(StudioAuditEvent event);}The Studio logs save / publish / import / export events. Your host can surface them to admins for traceability or just discard them (no-op implementation).
StudioPhaseRepository
Some host backends keep phase definitions in a separate table /
collection alongside the .pose file (typically so phase queries
can hit secondary indexes directly without parsing the whole
document). The repository lets the Studio replace-all the phases
for a movement on every save.
abstract class StudioPhaseRepository { Future<void> replaceAll(String movementId, List<PhaseDefinition> phases); Future<List<PhaseDefinition>> list(String movementId); Future<void> deleteForMovement(String movementId);}For hosts that don’t need this (the .pose file already carries
phases), a no-op implementation is fine.
StudioVersionRepository
Snapshot history for rollback.
abstract class StudioVersionRepository { Future<void> snapshot(String movementId, Movement movement); Future<List<MovementSnapshot>> list(String movementId);}The Studio snapshots every save. Your host can expose “previous versions” in its UI or just discard (no-op).
InMemoryStudioPersistence
For tests, demos, and standalone Studio installations without a backend, ship in-package:
import 'package:pose_flow_studio/pose_flow_studio.dart';
final persistence = InMemoryStudioPersistence().bundle();Stores everything in a Map. Survives the app session but not
restarts.
StudioScope
Exposes the bundle to the widget tree via InheritedWidget:
import 'package:pose_flow_studio/pose_flow_studio.dart';
StudioScope( persistence: persistence, assetBasePath: 'pose_flow/', // optional, for web pose worker assets metadataPanelBuilder: (context, movementId) => YourMetadataPanel(), child: const PoseFlowStudioScreen(),)Hooks
persistence: required. The bundle.assetBasePath: optional. Where the web pose pipeline finds its WASM worker + weights. See web integration.metadataPanelBuilder: optional. The Studio renders your panel inside the Phases column’s Metadata slot (host-specific fields like movement name, body parts, image URLs). When null, the slot is hidden.
Wiring a cloud backend
A common shape: Firestore + Cloud Storage. Each adapter
implements one of the Studio ports:
class FirestoreStudioCatalog implements StudioCatalog { // Writes the Movement to // `movements/{id}/movement.pose` in Cloud Storage; stamps // `pose_file_url` + `studio_authored: true` on the Firestore // doc.}
class FirestoreStudioPhaseRepository implements StudioPhaseRepository { // Mirrors phases to a `phase_definitions` array on the // movement doc (for query indexing).}
class FirestoreStudioVersionRepository implements StudioVersionRepository { // Snapshots into `movements/{id}/studio_versions/{timestamp}`.}
class FirestoreStudioAudit implements StudioAudit { // Writes to a `studio_audit_events` collection.}A single factory wires them up:
StudioPersistence buildStudioPersistence() { return StudioPersistence( catalog: FirestoreStudioCatalog(), audit: FirestoreStudioAudit(), phases: FirestoreStudioPhaseRepository(), versions: FirestoreStudioVersionRepository(), );}The router then wraps the Studio route:
StudioScope( persistence: buildStudioPersistence(), metadataPanelBuilder: (ctx, id) => YourMetadataPanel(id: id), child: const PoseFlowStudioScreen(),)The adapter layer is the only cloud-aware code; the Studio package itself stays storage-agnostic.
Wiring a Drift / SQLite backend
For local-only authoring (no cloud), use Drift or any other Flutter SQL package, same shape, adapters implementing the Studio ports against the local DB. Useful for offline-friendly trainer tools or single-author desktop workflows.
Storage-agnostic guarantee
Search packages/pose_flow_studio/lib/ for firebase,
firestore, cloud_firestore, drift, sqflite, zero hits.
The package has zero awareness of any specific storage. This is
enforced by code review + the audit; future adapters slot in
purely additively.
Read next
- Building a Studio host , the full host-side wiring guide.
- Overview.
StudioScopeAPI reference.