`TrackingResult`
The per-frame output of MovementTracker.processFrame.
Carries the scalar state your UI cares about (rep count, form score)
plus the raw inputs that produced it (the Pose, the per-channel
tracking values) so consumers can render without re-running pose
detection.
Returned by processFrame and forwarded through
TrackedMovementView.onTrackingResult
and ShowcaseTracker.frame.value.
Fields
class TrackingResult { final String? phaseId; final int repCount; final double formScore; final List<String> feedback; final RepQuality? lastRepQuality; final bool repJustCompleted; final Map<String, double> trackingValues; final RepPipelineSnapshot pipeline; final Pose? pose;}| Field | Meaning |
|---|---|
phaseId | Current phase id from the state machine. null until the first phase transition fires. |
repCount | Number of completed reps in this session. Monotonic, reset() zeroes it. |
formScore | Live form score, 0–100, rolling weighted average across recent frames. |
feedback | List of currently-active feedback messages (the same text FormFeedbackEvent carries, deduplicated for display). |
lastRepQuality | Per-dimension quality breakdown for the last completed rep. null until the first rep completes. Populated only on the frame repJustCompleted == true, track separately for cumulative quality. |
repJustCompleted | true on the single frame a rep transitions to “done”. Use this to trigger one-shot effects (counter bump, haptic). |
trackingValues | All per-channel scalar values this frame, keyed by TrackingPoint.id. Includes angles, distances, ratios, positions, velocities, and stabilities. |
pipeline | RepPipelineSnapshot, surfaces scoring-gate state for trainer-facing UIs. |
pose | The Pose this result was computed from. Surfaced so PIP overlays and skeleton painters can read landmarks without re-running pose detection. Nullable so synthetic / test results stay cheap to construct. |
RepQuality
When lastRepQuality is non-null, it carries the per-dimension
breakdown of the rep that just completed:
class RepQuality { final double score; // 0–100 overall final RepGrade grade; // perfect / great / good / okay / poor final double formScore; // form-rule compliance final double? romScore; // range-of-motion fullness final double? tempoScore; // movement controlled, not jerky final double? stabilityScore; // steady, not shaky final Duration duration; final List<FormViolation> violations;}| Field | Meaning |
|---|---|
score | Overall 0–100, weighted across the per-dimension components. |
grade | RepGrade enum: perfect (95+), great (85+), good (70+), okay (50+), poor (< 50). |
formScore | Rule-based compliance, penalties for triggered violations. |
romScore | How fully the rep traversed the authored phase sequence. |
tempoScore | Whether the rep stayed in the “controlled” duration band (< 1 s is flagged as momentum; the ideal band is 2–4 s). |
stabilityScore | Whether tracking-point values held steady or wobbled. |
duration | Wall-clock duration of the rep. |
violations | All FormViolations that fired during the rep. |
shouldCount (a RepQuality getter) returns true when score ≥ 50 ,
the default threshold the optional RepScoringConfig
gate uses unless overridden.
RepPipelineSnapshot
class RepPipelineSnapshot { final RepScoringConfig scoringGate; final bool lastRepCleared;}| Field | Meaning |
|---|---|
scoringGate | The currently-active RepScoringConfig. Trainers can see at a glance whether scoring is enabled. |
lastRepCleared | Whether the previous rep cleared the scoring gate (i.e. would have counted under the active gate). Trainers can spot reps the gate is silently rejecting. |
Patterns
Bump a rep counter
StreamBuilder<TrackingResult>( stream: tracker.results, builder: (context, snap) { final reps = snap.data?.repCount ?? 0; return Text('$reps', style: counterStyle); },);Show the per-rep quality breakdown
lastRepQuality is only non-null on the completion frame. Capture it
into your own state when repJustCompleted is true:
RepQuality? _latestQuality;
void _onTrackingResult(TrackingResult r) { if (r.repJustCompleted && r.lastRepQuality != null) { setState(() => _latestQuality = r.lastRepQuality); }}Read a specific tracking-point value
final kneeAngle = result.trackingValues['knee_angle'];if (kneeAngle != null) { // render}The keys are the TrackingPoint.id values authored in your .pose
file.
Read next
MovementTracker, the producer.Pose, the underlying per-frame pose.FormFeedbackEvent, the stream event form rules emit.RepScoringConfig, the rep- quality gate.