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.