Quick start, track a squat in 30 lines
Drop a tracked movement view into any Flutter screen and start counting
reps against a bundled .pose file.
1. Get a .pose file
For this quick start, drop any authored .pose file into your
app’s assets/ directory, or
author one with PoseFlow Studio.
A bundled squat fixture is the easiest way to verify the integration
end-to-end before authoring your own.
A .pose file is a single JSON document with formatVersion
containing every phase, tracking point, and form rule the runtime
needs. See the .pose file spec
for the exact shape.
2. Mount TrackedMovementView
TrackedMovementView is the single drop-in widget that does the full
loop, camera, pose detection, rep counting, form analysis, skeleton
overlay. You hand it a Movement (the in-memory model parsed
from your .pose file) and subscribe to its callbacks.
import 'dart:convert';import 'package:camera/camera.dart';import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'package:pose_flow/pose_flow.dart';
class SquatPage extends StatefulWidget { const SquatPage({super.key, required this.cameras}); final List<CameraDescription> cameras;
@override State<SquatPage> createState() => _SquatPageState();}
class _SquatPageState extends State<SquatPage> { Movement? _exercise; int _reps = 0; double _formScore = 100;
@override void initState() { super.initState(); _loadExercise(); }
Future<void> _loadExercise() async { final raw = await rootBundle.loadString('assets/squat.pose'); setState(() { _exercise = Movement.fromJson( jsonDecode(raw) as Map<String, dynamic>, ); }); }
@override Widget build(BuildContext context) { final movement = _exercise; if (movement == null) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } return Scaffold( body: Stack( children: [ TrackedMovementView( cameras: widget.cameras, movement: movement, onRepCompleted: (event) { setState(() => _reps = event.repNumber); }, onTrackingResult: (result) { setState(() => _formScore = result.formScore); }, ), Positioned( top: 60, left: 24, child: Text( 'Reps: $_reps Form: ${_formScore.toStringAsFixed(0)}', style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, ), ), ), ], ), ); }}That’s the full integration. Point the camera at yourself, do a squat, the counter ticks.
What just happened
Movement.fromJsonparsed the.posefile into an in-memory movement (phases, tracking points, form rules, rep-detection strategy).TrackedMovementViewcreated aMovementTrackerinternally, loaded the movement via the canonicalLoadMovementArgs.from(...).applyTo(tracker)builder, and wired the pose-detection pipeline.- Every frame: the camera emits a
Pose→ tracker processes it → the phase machine advances → the rep counter ticks when a phase sequence completes →onRepCompletedfires. - The same per-frame result carries the live form score, position
label, phase id, and a
posefield with the raw landmarks if you need them.
Next steps
- Author your own movement: Studio authoring workflow.
- Understand what the tracker is doing: Tracking pipeline.
- Hook into form feedback: Form analysis.
- Customise the camera UI: replace
TrackedMovementViewwithPoseCameraView+ your ownMovementTracker.