`ShowcaseTracker`
ShowcaseTracker is a thin facade over MovementTracker
shipped in package:pose_flow_studio. It exists so trainer-facing
surfaces (the PoseFlow Studio editor,
any CMS that embeds it) can subscribe to per-frame state via a
ValueNotifier<TrackingFrame> instead of wiring per-frame
callbacks by hand, ValueListenableBuilder rebuilds only the
widgets that bind to it, leaving the rest of the tree alone.
Consumer apps that just want “load a movement, count reps” don’t
need ShowcaseTracker, use TrackedMovementView
or MovementTracker directly. The
underlying rep-counting math is identical.
import 'package:pose_flow_studio/pose_flow_studio.dart';
final tracker = ShowcaseTracker(preset: MovementTrackerPreset.standard);tracker.loadMovement(movement);
// Per-frame UI bound via the notifierValueListenableBuilder<TrackingFrame>( valueListenable: tracker.frame, builder: (context, frame, _) { return Text('Reps: ${frame.repCount}'); },);
// Per-frame pose dispatch from your camera surfacetracker.processFrame(pose, videoWidth: w, videoHeight: h);One instance per “tracking session”, create when a screen mounts,
dispose() when it unmounts.
Constructor
ShowcaseTracker({ MovementTrackerPreset preset = MovementTrackerPreset.standard,});Internally instantiates a MovementTracker.withPreset(preset) and
subscribes to its onRepCompleted + onFeedback streams so it can
re-broadcast them through its own controllers.
Loading a movement
void loadMovement( Movement movement, { MovementConfig? configOverride,});Same semantics as MovementTracker.load, the optional
MovementConfig override replaces
the movement’s inlined values.
Per-frame dispatch
TrackingFrame? processFrame( Pose pose, { int? videoWidth, int? videoHeight, double? inferenceMs, bool? usedDetector, String? backboneBackend, String? decoderBackend, String? landmarkBackend,});Forwards pose straight to MovementTracker.processFrame, then
wraps the resulting TrackingResult in a TrackingFrame (which adds
the optional camera + timing fields above) and assigns it to
frame.value so the ValueNotifier fires.
The optional fields are surfaced into TrackingFrame for the
diagnostics HUD, they don’t affect tracking. Pass them when your
camera surface knows the values; pass null otherwise.
What’s a TrackingFrame?
It’s a TrackingResult plus the optional perf / camera fields:
class TrackingFrame { final Pose? pose; final int repCount; final double formScore; final String? phaseId; final List<String> feedback; final RepQuality? lastRepQuality; final bool repJustCompleted; final Map<String, double> trackingValues; final RepPipelineSnapshot pipeline; final int frameNumber; final int? videoWidth; final int? videoHeight; final double? inferenceMs; final bool? usedDetector; final String? backboneBackend; final String? decoderBackend; final String? landmarkBackend;}frameNumber increments monotonically on every processFrame call ,
useful for “skip every Nth frame” rendering optimisations.
”Pose lost” handling
void handlePoseLost();Wire this to your camera’s onPoseLost callback (or whatever signals
“the detector didn’t find a person this frame”). It nulls the pose on
the notifier so HUDs can flip to “looking for you…” without per-frame
callback complexity.
Events
Stream<RepCompletedEvent> get onRepCompleted;Stream<FormFeedbackEvent> get onFeedback;Re-broadcasts of the underlying MovementTracker streams. Subscribe
to these for one-shot effects (haptic on rep, voice cue on feedback);
use the notifier for sustained UI state.
Live tuning
void updateRepScoring(RepScoringConfig config);RepScoringConfig get repScoring;ScoringProfile get scoringProfile;updateRepScoring mirrors MovementTracker.updateRepScoring, the
Studio’s rep-scoring side-panel calls this on every slider movement
so changes take effect on the next rep without restarting the
session. It also writes the new gate into the in-memory Movement.repScoring
so you can persist the change later.
scoringProfile exposes the per-dimension weights the
ScoringProfileInferrer
derived for the loaded movement.
Disposal
Future<void> dispose();Always call this when the screen unmounts. Cancels the underlying
tracker’s stream subscriptions, closes both controllers, disposes
the MovementTracker, and disposes the notifier. Idempotent.
Read next
MovementTracker, the underlying tracker.ShowcaseTrackeris a thin wrapper.TrackedMovementView, the consumer-app convenience widget. Doesn’t depend onpose_flow_studio.TrackingResult, the per-frame shape the wrappedTrackingFrameis built from.