Integrating on mobile
Wiring PoseFlow into a native (iOS / Android) Flutter app.
Minimal integration
If you just want camera + rep counting against a loaded .pose
file:
import 'package:camera/camera.dart';import 'package:pose_flow/pose_flow.dart';
final cameras = await availableCameras();
TrackedMovementView( cameras: cameras, movement: yourLoadedMovement, onRepCompleted: (event) => print('Rep ${event.repNumber}'),);See the quick start for the full snippet.
Picking the camera lens
TrackedMovementView accepts initialCameraIndex, usually 0
(rear camera). For selfie-mode pose tracking (most fitness
apps), pick the front-facing camera:
final frontIndex = cameras.indexWhere( (c) => c.lensDirection == CameraLensDirection.front,);
TrackedMovementView( cameras: cameras, movement: movement, initialCameraIndex: frontIndex, // ...);Movement.cameraAngle (front / side / any) is an authoring
hint, NOT a lens hint, always use the front camera for live
tracking. The hint tells the bucket detector which authored band
to prefer when its confidence is low.
Permissions
iOS
ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key><string>This app needs the camera to track your movement.</string>Android
android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.INTERNET" /><uses-feature android:name="android.hardware.camera" android:required="true" />PoseFlow doesn’t request permissions itself, your app should
request them via permission_handler (or similar) before mounting
the camera widget.
Advanced, manual tracker wiring
For finer control (custom overlays, non-camera pose sources,
multi-tracker setups), drop down to MovementTracker +
PoseCameraView:
import 'package:pose_flow/pose_flow.dart';
class CustomTrackingPage extends StatefulWidget { // ...}
class _State extends State<CustomTrackingPage> { late final MovementTracker _tracker;
@override void initState() { super.initState(); _tracker = MovementTracker.withPreset(MovementTrackerPreset.standard); LoadMovementArgs.from( movement: widget.movement, movementConfig: widget.config, ).applyTo(_tracker);
_tracker.onRepCompleted.listen((event) { // your handler }); }
@override void dispose() { _tracker.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return PoseCameraView( cameras: widget.cameras, initialCameraIndex: widget.frontCameraIndex, onPoseUpdate: (pose) { if (pose == null) return; final result = _tracker.processFrame(pose); // Read result.repCount, result.formScore, etc. }, // Your custom overlay onCameraReady: () => debugPrint('camera ready'), ); }}PoseCameraView handles the camera lifecycle + pose engine + the
skeleton overlay. You handle everything downstream.
Tracker presets
| Preset | Use case |
|---|---|
casual | Looser thresholds, more forgiving anti-cheat. For demos + first-time users. |
standard | The default. Tuned for everyday fitness apps. |
strict | Tighter range gates, stricter rep validation. For coached sessions. |
competition | Maximum strictness. Competition-grade scoring. |
Use MovementTrackerConfig for full control over individual
sub-system knobs.
Common playback patterns
Picture-in-picture coaching
TrackedMovementView mounted in a small PIP overlay (camera in the
corner, training video filling the main view). Rep counts feed your
set-progress UI; form cues fire as toasts above the PIP.
Two-device competitive flow
Phone runs the tracker (TrackedMovementView); TV or tablet shows
the spectator view. The tracking device sends rep events over your
preferred transport (WebSocket, BLE, etc.); the spectator renders the
count + scoreboard. Many competitive UXs suppress the form score and
surface only the rep count, see Rep counting parity
for the policy options.
Headless scoring
Wire MovementTracker directly without a UI, feed it Pose frames
from any source (a non-camera replay, a recorded session, a server-
side pipeline), subscribe to onRepCompleted, write the result to
your scoring backend. No widgets, no rendering.
Test setup
Synthetic Pose fixtures + the static MovementTracker.processFrame
API let you drive the tracker without a camera. Useful for:
- Pinning rep-counting contracts against known input.
- Regression tests when authoring new movements.
- CI smoke tests for the consumer app’s tracking integration.
Read next
- Quick start.
- Web integration, for cross-platform apps.
- Rep counting parity across surfaces, the contract.
TrackedMovementView.MovementTracker.