`MovementConfig`
MovementConfig is the full tracker-side runtime view of an
movement. Movement (the on-disk .pose document) decodes into an
MovementConfig via Movement.toExerciseConfig(); consumers can
also supply their own MovementConfig as a runtime override
when loading a movement, which lets a published config (e.g. a CMS
Firestore hotfix) take precedence over the inlined values in the
.pose file without re-uploading anything.
tracker.load( movement, configOverride: publishedConfig, // overrides movement's inlined values);When to use the override
The override path exists for one reason: changing tracker behaviour
in production without re-deploying the .pose file. Typical
use cases:
- A trainer notices a movement is rejecting borderline reps and
wants to soften the knee-angle band, push a new
MovementConfigto Firestore, every consumer picks it up on next load. - A new form rule needs to ship to existing users, add it to the published config, no app update required.
- A bucket-specific
AngleBandwas authored wrong, fix the band in the published config, leave the original.poseimmutable.
For greenfield authoring (creating a new movement in Studio),
you don’t need the override. Studio writes the inlined values
straight into the .pose file via Movement.fromJson, and tracker
loads pick those up automatically.
Shape
class MovementConfig { final String id; final String version; final String name; final String? description; final String? category; final String? difficulty; // 'beginner' / 'intermediate' / 'advanced' final CameraAngle cameraAngle; // front / side / any
final MovementMetadata metadata; final LandmarkRequirements landmarks; final List<TrackingPoint> trackingPoints; final List<PhaseConfig> phases; final RepDetectionConfig repDetection; final List<FormRule> formRules; final AccessibilityConfig accessibility; final ScoringConfig scoring; final RepScoringConfig repScoring;}Fields
| Field | Meaning |
|---|---|
id / version / name | Identity. version is the schema version, not the movement version. |
description, category, difficulty | Display metadata; the runtime doesn’t act on them. |
cameraAngle | High-level enum (front / side / any) used by the FrameValidator’s view-angle gate. Authored at the movement level, different from the 16-bucket CameraAngleDetector output. |
metadata | MovementMetadata, author, dates, tags. |
landmarks | LandmarkRequirements, required / optional landmarks + fallback alternatives. Frame validator uses this. |
trackingPoints | The full TrackingPoint list. Per-frame channel evaluations. |
phases | The list of PhaseConfigs the state machine walks. |
repDetection | RepDetectionConfig, which strategy to use (cycle / sequence / compound / hold), which phase counts as “rep complete”, optional duration bounds. |
formRules | List of FormRules the form service evaluates each frame. |
accessibility | AccessibilityConfig, high-contrast, larger text, voice-over preferences. The SDK doesn’t render UI; consumers honour this. |
scoring | ScoringConfig, per-dimension scoring weights for the trainer-facing badge. Different from repScoring (which gates rep validity). |
repScoring | RepScoringConfig. Defaults to RepScoringConfig.disabled. |
Override semantics
When you pass configOverride to MovementTracker.load, the entire
config object replaces the one decoded from the movement, there’s no
field-level merge. If you only want to override one field, copy the
movement’s decoded config first:
final base = movement.toExerciseConfig();final overridden = base.copyWith( repScoring: const RepScoringConfig(enabled: true, minQualityScore: 70),);
tracker.load(movement, configOverride: overridden);copyWith is the safe path, it preserves every field you don’t
explicitly change.
JSON
MovementConfig round-trips via toJson / fromJson. The shape is
identical to the config block inside a .pose file (the .pose
file is essentially a Movement with an embedded MovementConfig
plus the pose-frame data and camera buckets). See the
.pose file spec.
Patterns
Publish a config to Firestore
CMS publishes to /movements/{id}/configs/{version} as JSON. Mobile
apps fetch the latest config on session start and feed it to the
tracker:
final configJson = await firestore .collection('movements').doc(movementId) .collection('configs').doc('latest').get();final config = MovementConfig.fromJson(configJson.data()!);
tracker.load(movement, configOverride: config);Live-patch a single rule
For trainer-facing tools that need to tweak a rule without
restarting the session, prefer
MovementTracker.updateRepScoring ,
it handles the RepScoringConfig slice without forcing a full
load. For other slices (form rules, phases), unload and reload
with a new override.
Read next
Movement, the on-disk document the config is decoded from.MovementTracker,loadis where the override is applied.RepScoringConfig, the most commonly overridden slice..posefile spec.