Skip to content
Blog

Taking Photos and Video with the Flutter camera Package

The camera 0.12.2 plugin gives Flutter apps a live preview, stills, video recording and raw image streams — with a lifecycle you must manage yourself.

Published on • November 5, 2026

AI Assistant

If your app needs a live viewfinder, a shutter button and a record button rather than a system picker, the official camera plugin is the tool for the job. At version 0.12.2, published by the verified flutter.dev publisher, it supports Android SDK 24+, iOS 13.0+ and the web through camera_web.

Its feature list is short and concrete: display a live camera preview in a widget, capture snapshots to a file, record video, and stream raw image buffers into Dart.

Discovering cameras and creating a controller

Start by listing the hardware, then hand one CameraDescription to a CameraController together with a ResolutionPreset:

import 'package:camera/camera.dart';

late final List<CameraDescription> cameras;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  cameras = await availableCameras();
  runApp(const CameraApp());
}

CameraController _makeController() {
  return CameraController(
    cameras.first,                 // front or back camera
    ResolutionPreset.veryHigh,     // ~1080p target
    enableAudio: true,             // include audio when recording
    fps: 30,
  );
}

The ResolutionPreset values are targets, not guarantees: low (352x288 on iOS, ~240p on Android and web), medium (~480p), high (~720p), veryHigh (~1080p), ultraHigh (~2160p) and max (the highest available on that device). Platforms may fall back up or down when the exact preset is missing.

The initialize → use → dispose lifecycle

initialize() must complete before the preview renders, and permission errors arrive as CameraExceptions during that call:

Future<void> _initCamera(CameraDescription description) async {
  final controller = CameraController(description, ResolutionPreset.high);
  try {
    await controller.initialize();
    if (!mounted) return;
    setState(() => _controller = controller);
  } on CameraException catch (e) {
    switch (e.code) {
      case 'CameraAccessDenied':
      case 'CameraAccessDeniedWithoutPrompt': // iOS: previously denied
      case 'CameraAccessRestricted':          // iOS: parental controls
        openPermissionSettings();
      default:
        rethrow;
    }
  }
}

@override
void dispose() {
  _controller?.dispose(); // always release the camera
  super.dispose();
}

Two lifecycle rules matter. First, since version 0.5.0 the plugin no longer reacts to app lifecycle changes, so you must override didChangeAppLifecycleState and dispose on inactive, re-initializing on resumed. Second, dispose() is mandatory — the OS will not let a second app open a camera that your controller still holds.

Capturing photos, video and image streams

Once initialized, the controller exposes three capture paths. takePicture() returns an XFile; video is split across startVideoRecording() and stopVideoRecording(), which also returns an XFile; and startImageStream(onAvailable) pushes raw frames to Dart for ML or filtering, paired with stopImageStream():

Future<XFile> snap() => _controller.takePicture();

Future<XFile> recordClip() async {
  await _controller.startVideoRecording();
  await Future<void>.delayed(const Duration(seconds: 5));
  return _controller.stopVideoRecording();
}

Future<void> streamFrames() => _controller.startImageStream((frame) {
  // CameraImage buffers — run ML inference off the main isolate
});

Call supportsImageStreaming() before streaming: not every platform implements it, and background image streaming on Android needs extra steps in the CameraX implementation. Recording also supports pauseVideoRecording() / resumeVideoRecording(), while setFlashMode, setFocusPoint, setExposurePoint and setZoomLevel shape the preview itself.

Permissions and native setup

On iOS you must add both NSCameraUsageDescription and NSMicrophoneUsageDescription rows to ios/Runner/Info.plist. On Android the endorsed implementation is camera_android_camerax, built on CameraX for broader device support but with a documented limitations list; camera_android (Camera2) is available if you need to avoid those limits.

camera versus image_picker

image_picker (version 1.2.4) delegates everything to the operating system — PHPicker on iOS 14+, the Android Photo Picker on Android 13+ — and hands back an XFile from a gallery or a system camera UI. Use it when the user is supplying an existing photo or a quick snapshot. Use camera when your app owns the viewfinder: custom framing, filters, flash control, zoom, or frame-by-frame processing. The two are complementary, not competing.

Takeaway: The camera plugin gives you preview, stills, video and frame streaming behind one controller — but only if you handle permission errors and app lifecycle yourself and always dispose the controller when the screen goes away.