Environment
- camerawesome: 2.5.0 (latest on pub.dev as of 2026-05-31)
- Flutter: 3.x (stable)
- Device: iPhone 16 Plus, iOS 18.x
CamerawesomePlugin.isMultiCamSupported() returns true
Symptom
Using SensorConfig.multiple(...) on either CameraAwesomeBuilder.awesome or CameraAwesomeBuilder.custom floods the log with:
FlutterError: Multiple widgets used the same GlobalKey.
The key [GlobalKey#xxxxx] was used by multiple widgets. The parents of those widgets were:
- Padding(padding: EdgeInsets.zero, dependencies: [Directionality], ...)
- Padding(padding: EdgeInsets.zero, dependencies: [Directionality], ...)
A GlobalKey can only be specified on one widget at a time in the widget tree.
Visible effect: both the primary preview and the PiP preview render black (or one of them does). On capture, the primary sensor file is never written; only the secondary (PiP) sensor produces a file. The capture event's multiple.fileBySensor is missing the primary entry.
Root cause
lib/src/widgets/preview/awesome_preview_fit.dart:7:
final previewWidgetKey = GlobalKey();
This is a module-level singleton GlobalKey. It is then assigned to the wrapping InteractiveViewer on line 159 of the same file:
InteractiveViewer(
key: previewWidgetKey,
transformationController: transformController,
scaleEnabled: false,
constrained: false,
panEnabled: false,
...
)
When SensorConfig.multiple is in effect, the package mounts PreviewFitWidget twice (once for the primary preview, once for the PiP overlay). Both instances attach the same module-level GlobalKey, which Flutter rejects: a GlobalKey must be unique in the widget tree at any given time.
The resulting tree-finalize assertion appears to be what prevents the second AVCaptureMultiCamSession output from binding correctly on iOS — only the secondary sensor produces a capture file.
Reproduction
The package's own example/lib/multi_camera.dart should reproduce this on any iOS 13+ device with multi-cam support (iPhone XS or later).
sensorConfig: SensorConfig.multiple(
sensors: [
Sensor.type(SensorType.telephoto), // matches the example's iOS branch
Sensor.position(SensorPosition.front),
],
flashMode: FlashMode.none,
aspectRatio: CameraAspectRatios.ratio_4_3,
),
Suggested fix
The InteractiveViewer's scaleEnabled and panEnabled are both false in this code path. The GlobalKey does not appear to be load-bearing for the InteractiveViewer's state — it can either be removed, or scoped per-instance by making it a final _previewWidgetKey = GlobalKey() inside _AnimatedPreviewFitState (or similar per-mount scope).
If the key is needed elsewhere (e.g., for transformationController coordination with another widget I haven't traced), the simplest correctness fix is to make it per-instance.
Happy to send a PR if a maintainer can confirm which scope is intended.
Environment
CamerawesomePlugin.isMultiCamSupported()returnstrueSymptom
Using
SensorConfig.multiple(...)on eitherCameraAwesomeBuilder.awesomeorCameraAwesomeBuilder.customfloods the log with:Visible effect: both the primary preview and the PiP preview render black (or one of them does). On capture, the primary sensor file is never written; only the secondary (PiP) sensor produces a file. The capture event's
multiple.fileBySensoris missing the primary entry.Root cause
lib/src/widgets/preview/awesome_preview_fit.dart:7:This is a module-level singleton GlobalKey. It is then assigned to the wrapping
InteractiveVieweron line 159 of the same file:When
SensorConfig.multipleis in effect, the package mountsPreviewFitWidgettwice (once for the primary preview, once for the PiP overlay). Both instances attach the same module-levelGlobalKey, which Flutter rejects: aGlobalKeymust be unique in the widget tree at any given time.The resulting tree-finalize assertion appears to be what prevents the second
AVCaptureMultiCamSessionoutput from binding correctly on iOS — only the secondary sensor produces a capture file.Reproduction
The package's own
example/lib/multi_camera.dartshould reproduce this on any iOS 13+ device with multi-cam support (iPhone XS or later).Suggested fix
The
InteractiveViewer'sscaleEnabledandpanEnabledare bothfalsein this code path. TheGlobalKeydoes not appear to be load-bearing for the InteractiveViewer's state — it can either be removed, or scoped per-instance by making it afinal _previewWidgetKey = GlobalKey()inside_AnimatedPreviewFitState(or similar per-mount scope).If the key is needed elsewhere (e.g., for
transformationControllercoordination with another widget I haven't traced), the simplest correctness fix is to make it per-instance.Happy to send a PR if a maintainer can confirm which scope is intended.