Skip to content

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

PresetUse case
casualLooser thresholds, more forgiving anti-cheat. For demos + first-time users.
standardThe default. Tuned for everyday fitness apps.
strictTighter range gates, stricter rep validation. For coached sessions.
competitionMaximum 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.