iris_camera

πŸ“Έ iOS + Android + Web camera toolkit for Flutter, powered by AVFoundation, CameraX, and browser MediaDevices API. Render the native preview, switch lenses, stream frames, capture photos, record video, tune exposure/white balance/torch/zoom, and listen to lifecycle + orientation + AF/AE state – all from Dart.

Platform coverage: iOS + Android + Web. Other platforms no-op safely.


Highlights

  • πŸ” Lens discovery & switching – list every lens (front included by default; exclude with includeFrontCameras: false) and reconfigure with switchLens.
  • πŸ–ΌοΈ Native preview widget – IrisCameraPreview wraps AVCaptureVideoPreviewLayer with tap-to-focus + overlay hooks.
  • πŸ“Έ Still capture – capturePhoto with flash/ISO/exposure overrides. Long exposure is supported; query the device max via getMaxExposureDuration.
  • πŸ“Έ Burst – captureBurst(count, options) supports long exposure/ISO overrides, optional file saving (directory, filenamePrefix), and progress events via burstProgressStream.
  • πŸŽ›οΈ Pro controls – focus mode/point, exposure mode/point/EV, white balance, frame rate range, torch, zoom, resolution presets.
  • πŸ“‘ Streams – live BGRA image stream, orientation stream, lifecycle state stream, AF/AE state stream.
  • πŸ”§ Lifecycle – explicit initialize/pause/resume/dispose and structured errors via IrisCameraException.
  • πŸŽ₯ Video – start/stop file-based recording (iOS/Android), optional audio.

Install

Supported platforms

  • Android: minSdk 26+, targetSdk 34 (CameraX 1.3.x)
  • iOS: iOS 15.0+
  • Web: Modern browsers with MediaDevices API support (Chrome, Firefox, Safari, Edge)
flutter pub add iris_camera
import 'package:iris_camera/iris_camera.dart';

final camera = IrisCamera();
final lenses = await camera.listAvailableLenses(); // includeFrontCameras defaults to true
await camera.switchLens(lenses.first.category);
final photo = await camera.capturePhoto(
  options: const PhotoCaptureOptions(flashMode: PhotoFlashMode.auto),
);

Live preview:

final focusController = FocusIndicatorController();

IrisCameraPreview(
  aspectRatio: 3 / 2,
  enableTapToFocus: true,
  showFocusIndicator: true,
  onTapFocus: (point) => camera.setFocus(point: point),
  focusIndicatorController: focusController,
);

iOS setup

Add to ios/Runner/Info.plist (both are required or the app will crash when accessing camera/mic):

<key>NSCameraUsageDescription</key>
<string>This app needs the camera to capture photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs the microphone for recording video with audio.</string>

That’s it. Permissions are requested automatically on first use.

Exclude front cameras by calling listAvailableLenses(includeFrontCameras: false).

Android setup

Add the camera permission to your app manifest (the plugin also declares it for you):

<uses-permission android:name="android.permission.CAMERA" />
<!-- Needed for video with audio -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />

iris_camera will prompt for runtime permission automatically before accessing the camera. The preview is rendered via a native PreviewView, and tap-to-focus works the same as iOS.

Web setup

No additional configuration required. The browser will automatically prompt for camera permission when accessing the camera. Ensure your site is served over HTTPS (required for camera access).

Note: Some advanced features have limited support on web:

  • Focus/exposure point control is simulated (browser limitation)
  • White balance temperature/tint is not available
  • Video recording outputs WebM format (blob URL)
  • Torch/flash depends on browser and device support

API quick reference

Key methods:

  • listAvailableLenses({includeFrontCameras}) β†’ List<CameraLensDescriptor>
  • switchLens(CameraLensCategory category) β†’ CameraLensDescriptor
  • capturePhoto({PhotoCaptureOptions options}) β†’ Uint8List
  • captureBurst({count, PhotoCaptureOptions options, directory, filenamePrefix}) β†’ List<Uint8List> or saved file paths when directory is set
  • burstProgressStream β†’ BurstProgressEvent(total, completed, status, error?)
  • getMaxExposureDuration() β†’ Duration (use to clamp long exposures)
  • startVideoRecording({filePath, enableAudio}) β†’ String path
  • stopVideoRecording() β†’ String path
  • Focus: setFocus(point/lensPosition), setFocusMode, focusExposureStateStream
  • Exposure: setExposureMode, setExposurePoint, setExposureOffset, getMin/MaxExposureOffset, getExposureOffsetStepSize
  • Zoom/torch/WB: setZoom, setTorch, setWhiteBalance
  • Frame/format: setFrameRateRange, setResolutionPreset
  • Streams: imageStream, orientationStream, stateStream
  • Lifecycle: initialize, pauseSession, resumeSession, disposeSession
  • Errors: IrisCameraException(code, message, details)

Data classes:

  • CameraLensDescriptor (id, name, position, category, supportsFocus, optional focalLength, fieldOfView)
  • PhotoCaptureOptions (flashMode, exposureDuration, iso)
  • OrientationEvent, CameraStateEvent, FocusExposureStateEvent, IrisImageFrame

Widget:

  • IrisCameraPreview with tap-to-focus + focus indicator styling/control.

iris_camera vs camera (iOS/Android)

Capability iris_camera camera
Still photos βœ… Shared session JPEG capture βœ…
Live preview widget βœ… IrisCameraPreview (iOS/Android/Web) βœ…
Lens discovery/switching βœ… Enumerate + switch by category (wide/ultraWide/telephoto/etc.), front opt-in βšͺ️ List only (no switching API)
Tap/manual focus βœ… Tap/point focus; iOS also supports lensPosition βœ…
Exposure controls βœ… mode/point/EV/ISO/exposure duration βœ… (mode/point/offset)
White balance override βœ… iOS: temperature/tint; Android: auto/lock only βšͺ️ (not exposed)
Zoom βœ… βœ…
Torch βœ… (torch separate from flash) βœ…
Frame rate range βœ… min/max FPS βšͺ️ limited
Resolution preset βœ… βœ…
Live image stream βœ… BGRA βœ…
Orientation stream βœ… device/video βœ…
AF/AE state stream βœ… βšͺ️ basic focus/exposure mode only
Lifecycle controls βœ… initialize/pause/resume/dispose + state stream βœ… (controller init/dispose)
Video recording βœ… (iOS/Android/Web) βœ…
Web βœ… (MediaDevices API) βœ…

Example flow

final lenses = await camera.listAvailableLenses();
final tele = lenses.firstWhere(
  (lens) => lens.category == CameraLensCategory.telephoto,
  orElse: () => lenses.first,
);

await camera.switchLens(tele.category);
await camera.initialize();
camera.stateStream.listen((event) => debugPrint('state=${event.state}'));
camera.focusExposureStateStream.listen((event) => debugPrint('af/ae=${event.state}'));

await camera.setExposureMode(ExposureMode.locked);
await camera.setFocusMode(FocusMode.locked);
final photo = await camera.capturePhoto();

License

MIT β€” see LICENSE.

Libraries

iris_camera