NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
Hands-free front + back photo capture with location and timestamp — native simultaneous capture where the hardware allows it, sequential everywhere else, and the same composed layout either way.
Last release 1 months ago
17 Aug 2026
Too new to tell
only 2 release windows
Nearly every release is documented
notes for 5 of 5 stable releases
Nothing withdrawn
no release was ever pulled
2 months old
5 releases · first in 2026
One column per month.
The capture flow now composes the layout itself: `result.composedPhoto`. Previously onComplete handed back two raw photos, so an app that saved or upl
result.composedPhoto. Previously onComplete handed back two raw
photos, so an app that saved or uploaded what it was given shipped the back
camera's shot alone — the selfie, map and timestamp only existed if the
caller went on to mount DualShotView or call composeDualShot itself.
The flow now renders the finished layout before it completes and puts the
PNG on the result. Pass compose: DualShotStyle.light (or any style) to
restyle it, or compose: null for the old behaviour — the two raw photos
are still on the result either way, and a failed compose leaves
composedPhoto null rather than losing the capture.DualCaptureLabels.composing for the extra beat this adds to the
finishing screen.pubspec.yaml
plugin comment still said all capture was pure Dart and the only native
code was a capability probe (the simultaneous path has been a native
session since 0.5.0). The example README still described a shutter-less
flow (0.6.0 added the tap to start) and a language toggle the example
doesn't have; the result-layout docs now say which field holds the composed
image.Camera permission is now actually requested on the simultaneous path. The native session binds the cameras directly, so nothing ever showed the OS cam
camera plugin's request on the fallback path too, so users only ever
saw the location popup. The plugin now requests CAMERA itself before
starting the native session (Android and iOS), and the location request
waits until a camera is live so the two dialogs never collide. A denial
shows the existing retry screen.getLastKnownPosition
fallback is gone: it could stamp a shot with a fix from hours ago (or the
emulator's default). A capture now carries a real current fix or a null
latitude/longitude, never a guess.DualCaptureLabels.tapToStart hint;
everything after the tap is automatic as before — the back page still
counts down on its own, since the user's hands are busy turning the
phone.ValueNotifier instead of whole-page setState), and each
live preview sits behind its own RepaintBoundary so overlay repaints
never touch the camera layers. The composed view's back photo uses
gaplessPlayback, so restyling never flashes an empty frame.DualCaptureLabels; pass your own
to reword or translate.CameraManager.getConcurrentCameraIds(), which is empty on plenty of
hardware (and the emulator) that streams front+back just fine — so auto
said "this device can't" on devices that could. The probe now checks
exactly what CameraX's concurrent bindToLifecycle checks:
FEATURE_CAMERA_CONCURRENT. The same wrong gate inside the native
session (availableConcurrentCameraInfos) is gone too; actually binding
the cameras remains the final arbiter, so overclaiming devices still fall
back cleanly.DualShotView
used to cover-fit the photo into whatever box its parent gave it, which
shaved the top and bottom off every shot whose aspect didn't match — read
as "the footer cut off the bottom of my photo". The card now takes its
shape from the photo itself (full photo, footer below) and scales down as
a whole when the parent is shorter. New DualShotView.boundaryKey puts
the saveComposedDualShot boundary on the card itself so the saved PNG
has no empty margins; wrapping the view in your own RepaintBoundary
still works.resolutionInfo).debugPrinted instead of
swallowed silently, so "why did it fall back?" is answerable from logs.Fix initialize() throwing Can't create handler inside thread … Looper.prepare() on Android: Flutter's texture registry must be used from the platform
initialize() throwing Can't create handler inside thread … Looper.prepare()
on Android: Flutter's texture registry must be used from the platform main
thread, and the plugin was calling it from its worker. Texture create/release
now hop to the main thread; an integration test covers the full
initialize → preview → dispose cycle on a device.Low-RAM (Android Go) devices always take the sequential path and cap stills at ~8MP, so a second camera pipeline never competes for memory.
DualCameraController — live front/back preview textures, capturePhoto() and startRecording()/stopRecording().
DualCameraController — live front/back preview textures, capturePhoto()
and startRecording()/stopRecording().AVCaptureMultiCamSession (A12+), falling back to one camera at a time
everywhere else. Sequential video records the back clip, then re-records the
front for the same duration.stage reports which camera a sequential capture is on, so apps can say
"Taking back photo…" / "Taking front photo…".DualLayoutStyle — one object holding every visual knob, shared by
DualLayoutView, DualCameraPreview and DualCaptureView so a viewfinder
and its result are configured identically. Picture-in-picture, side-by-side,
stacked and primary-only arrangements, or a builder for anything else.BoxFit.contain, so the whole frame is visible
rather than cropped, with background filling the letterbox.DualCameraLabels — every user-facing string with English defaults and a
statusFor(controller) that picks the right one, for wording and localisation.DualStorage — captures land in the cache directory by default; set a
directory and/or nameBuilder to have them filed automatically.AdaptiveDualCamera.capture() / .record() for one-shot use without a preview.Your coding agent can read these notes before it upgrades. Set up the MCP server →