`PoseCameraView`
The camera primitive. Wraps the platform’s camera plugin, runs the
pose engine, emits a Pose per frame, and paints the skeleton
overlay. The pose it emits is in display space, orientation-
rotated, selfie-mirrored on the front camera, normalised to [0, 1]
against the post-rotation buffer dimensions.
Use PoseCameraView directly when you want camera + pose detection
WITHOUT movement tracking, a calibration tool, a demo, a custom
scoring rig that bypasses MovementTracker.
For the common “load a .pose file + count reps” case, prefer
TrackedMovementView which wraps
this widget and feeds the pose into a tracker for you.
Minimal usage
final cameras = await availableCameras();
PoseCameraView( cameras: cameras, onPoseUpdate: (pose) { if (pose != null) { // pose.points[i] gives Offset (x, y) in [0, 1] display space } },)That gives you a full-bleed camera preview, a green skeleton overlay,
and a per-frame Pose callback.
Constructor
const PoseCameraView({ required List<CameraDescription> cameras, void Function(Pose? pose)? onPoseUpdate, void Function(String message)? onError, VoidCallback? onCameraReady, bool showSkeleton = true, bool showControls = false, int? initialCameraIndex, ResolutionPreset resolutionPreset = ResolutionPreset.medium, bool pipelined = true, BlazeFlowBackend? backend, ExecutionConfig? executionConfig, int isolateCount = 1, bool syncedFrameMode = false, int displayFrameMaxDimension = 480, bool preferCpu = false, void Function(double inferenceMs, bool usedDetector)? onInferenceTiming, void Function(PipelineTimingReport report)? onPipelineTiming, VoidCallback? onCameraFrame, void Function(String backbone, String decoder, String landmarkHead)? onBackends, void Function(int width, int height)? onImageSize,});onPoseUpdate, the canonical pose surface
There is one pose callback. The Pose it delivers is in display
space:
- Orientation-rotated to match the device orientation. On a portrait phone with a landscape-native sensor, landmarks land in the rotated frame, not the sensor buffer.
- Selfie-mirrored on the front camera. When the user sees their
right hand on the right side of the screen,
landmark.dxfor that hand is on the right side of[0, 1]. The pose matches what the user sees. - Normalised to
[0, 1]against the post-rotation dimensions.
This is the same coordinate space the skeleton overlay paints in, the
same space MovementTracker consumes via
TrackedMovementView, and the same
space PoseFlow Studio authors .pose thresholds against. Authoring
and playback agree on coordinate scale by construction.
initialCameraIndex
Defaults to 0, which is usually the rear camera. For a selfie / mirror-mode pose tracker, walk the camera list and pick the front- facing one:
final frontIndex = cameras.indexWhere( (c) => c.lensDirection == CameraLensDirection.front,);PoseCameraView(cameras: cameras, initialCameraIndex: frontIndex, ...);resolutionPreset
Default ResolutionPreset.medium (~720 × 480 on most devices). The
pose engine downsamples to ≤ 448 px internally, so higher capture
resolutions only pay plane-copy + downscale cost. Bump to high
(~1280 × 720) only when you genuinely need the extra pixels, e.g.
when recording a reference video alongside.
The package:camera plugin caps capture at ~30 fps on every preset on
Android; a Camera2 native config is required to unlock 60 fps for
fast-motion movements.
pipelined
When true (default), the widget delivers the PREVIOUS frame’s pose while the current frame is in the engine, masks the ~15 ms pose inference latency, the skeleton appears smooth. Set false for minimum end-to-end latency on devices where you can guarantee the engine finishes within the frame budget.
backend, executionConfig, isolateCount, preferCpu
PoseFlow’s native runtime engine knobs. Defaults auto-select the best backend per device:
| Platform | Default backend |
|---|---|
| iOS / macOS | Metal |
| Android | NNAPI (landmark tail real; backbone CPU pending) + OpenCL on Adreno |
| Web | WebGL / WASM-SIMD |
preferCpu = true forces CPU even when accelerators are available ,
useful when a specific device hits NNAPI driver bugs.
syncedFrameMode
When true, the widget displays the PROCESSED frame (the same image the pose engine saw) instead of the live camera preview. Eliminates the perceptual lag between skeleton and image at the cost of ~1 frame of overall latency. Default false.
displayFrameMaxDimension (default 480) caps the synced-frame display
size, drop lower on low-end devices.
Diagnostics callbacks
Five callbacks surface per-frame perf data without forcing you to own the pose source yourself:
| Callback | Fires | What it carries |
|---|---|---|
onInferenceTiming(ms, usedDetector) | Every successful inference | pose-engine C-pipeline total ms + whether the heavy SSD detector ran this frame (vs cheap tracking reuse). |
onPipelineTiming(report) | Every successful inference | Per-stage PipelineTimingReport, color convert, isolate send, C inference, isolate receive, Dart tracking. |
onCameraFrame() | Every camera frame arrival | Lets you tick a Camera FPS counter independently of the inference rate, so you can tell whether the cap is the sensor or the pipeline. |
onBackends(backbone, decoder, landmarkHead) | Once after engine init | Short backend names, 'cpu', 'metal', 'nnapi', 'opencl', 'vulkan'. A silent driver-bind failure that drops to CPU is otherwise invisible. |
onImageSize(w, h) | Camera flip or orientation change | Post-rotation display dimensions, used by the studio’s outer AspectRatio so the skeleton overlay lands on the actual visible pixels. |
LiveDiagnosticsSheet consumes
all five of these to render the in-app perf panel.
What the widget renders
- Full-bleed camera preview (or processed frame in
syncedFrameMode). - Pose skeleton (when
showSkeleton: true), a stick figure painted on top of the preview, always in display space. - Camera switch + flash controls (when
showControls: true).
The skeleton is painted by PoseOverlay directly against the visible
image, so the bones land on the user. If you want a different overlay,
set showSkeleton: false and stack your own widget above
PoseCameraView.
Web specifics
On web, PoseCameraView uses an HTML <video> element + a Web Worker
for the pose pipeline. The worker needs four asset files served from a
URL prefix your host configures:
blazepose_worker.jsblazepose_pipeline.wasm+blazepose_pipeline.jsblazepose_detector_weights.binblazepose_optimized_weights_int8.bin
PoseFlow Studio consumers configure this via StudioScope.assetBasePath.
For standalone PoseCameraView usage on web, the asset path defaults
to web/pose_flow/. See Integrating on web
for the full setup.
Imperative reset
final key = GlobalKey<PoseCameraViewState>();
PoseCameraView(key: key, ...)
key.currentState?.reinitialize(); // re-pick the camera, re-init the engineUse after switching cameras or when the user grants / revokes permissions mid-session.
Read next
TrackedMovementView, wraps this with full movement tracking.Pose, the per-frame output.- Integrating on web, asset paths + worker setup.
PipelineTimingReport, per-stage timing data shape.