Flutter Camera Tutorial and Example
This is a Flutter Camera tutorial and example. You will learn how to captures images, preview them, record videos, pause/stop recordings, list captured images etc. The official Flutter camera package will be used.
A Flutter plugin for iOS, Android and Web allowing access to the device cameras.
Through this plugin you can:
- Display live camera preview in a widget.
- Snapshots can be captured and saved to a file.
- Record video.
- Add access to the image stream from Dart.
Step 1: Install it
The first step is to install this package. To do that go to your pubspec.yaml file and declare camera as a dependency:
dependencies:
camera: ^0.9.4+2
Then sync or flutter pub get.
Alternatively you can install it through the commandline:
$ flutter pub add camera
Step 2: Setup Platform
Depending on the platform you are targeting, you need to configure your app appropriately as follows:
Android
For Android you need to set the minimum Android sdk version to 21 (or higher) in your android/app/build.gradle file.
minSdkVersion 21
iOS
The camera plugin functionality works on iOS 10.0 or higher. If compiling for any version lower than 10.0, make sure to programmatically check the version of iOS running on the device before using any camera plugin features.
Add two rows to the ios/Runner/Info.plist:
- one with the key
Privacy - Camera Usage Descriptionand a usage description. - and one with the key
Privacy - Microphone Usage Descriptionand a usage description.
Or in text format add the key:
<key>NSCameraUsageDescription</key>
<string>Can I use the camera please?</string>
<key>NSMicrophoneUsageDescription</key>
<string>Can I use the mic please?</string>
Web
For web you should use the camera_web package.
Step 3: Write Code
Here is a simple example showing Camera usage:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
List<CameraDescription> cameras;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
runApp(CameraApp());
}
class CameraApp extends StatefulWidget {
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
CameraController controller;
void initState() {
super.initState();
controller = CameraController(cameras[0], ResolutionPreset.max);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
void dispose() {
controller?.dispose();
super.dispose();
}
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
return MaterialApp(
home: CameraPreview(controller),
);
}
}
Reference
Find the reference links below:
| Number | Link |
|---|---|
| 1. | Download Example |
| 2. | Read more |
Example 2:Full Fledged Camera App
A full customizable Camera example app but still easy to understand for learning purposes.
Form this app you will learn how to implement the following concepts:
- Capture quality selector
- Zoom control
- Exposure control
- Flash mode selector
- Button for flipping the camera — rear cam to front cam and vice versa
- Button for capturing an image
- Toggle for shifting from image mode to video mode
- Video mode controls — start, pause, resume, stop
- Last captured image or video preview
- Retrieve the image/video files
Here is the demo:

Step 1: Install Necessary packages
Three third party packages are used in this app:
- camera: provides cross-platform APIs for implementing the camera functionalities
- video_player: for previewing the captured videos
- path_provider: for storing the images or videos inside a directory from where they can be easily accessed
In your pubspec.yaml declare the dependencies follows:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
camera: ^0.9.4
video_player: ^2.2.5
path_provider: ^2.0.5
Step 2: Setup Platforms
Follow these steps only if you are creating a new project. If you are downloading or cloning the project then these steps have already been performed.
Android
Go to android/app/build.gradle Change the minSDK to 21+:
minSdkVersion 21
iOS
For iOS go to ios/Runner/Info.plist and add the following:
<key>NSCameraUsageDescription</key>
<string>Can I use the camera please?</string>
<key>NSMicrophoneUsageDescription</key>
<string>Can I use the mic please?</string>
Step 3: Create a Camera Screen
Create a folder known as screens and inside it create a camera_screen.dart and add the following imports:
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_camera_demo/screens/preview_screen.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';
import '../main.dart';
Create a class called CameraScreen as a StatefulWidget:
class CameraScreen extends StatefulWidget {
_CameraScreenState createState() => _CameraScreenState();
}
Then go ahead and create the State class for the above class:
class _CameraScreenState extends State<CameraScreen>
with WidgetsBindingObserver {
Define it's instance fields:
CameraController? controller;
VideoPlayerController? videoController;
File? _imageFile;
File? _videoFile;
// Initial values
bool _isCameraInitialized = false;
bool _isRearCameraSelected = true;
bool _isVideoCameraSelected = false;
bool _isRecordingInProgress = false;
double _minAvailableExposureOffset = 0.0;
double _maxAvailableExposureOffset = 0.0;
double _minAvailableZoom = 1.0;
double _maxAvailableZoom = 1.0;
// Current values
double _currentZoomLevel = 1.0;
double _currentExposureOffset = 0.0;
FlashMode? _currentFlashMode;
List<File> allFileList = [];
final resolutionPresets = ResolutionPreset.values;
ResolutionPreset currentResolutionPreset = ResolutionPreset.high;
The following function will refresh already captured images:
refreshAlreadyCapturedImages() async {
final directory = await getApplicationDocumentsDirectory();
List<FileSystemEntity> fileList = await directory.list().toList();
allFileList.clear();
List<Map<int, dynamic>> fileNames = [];
fileList.forEach((file) {
if (file.path.contains('.jpg') || file.path.contains('.mp4')) {
allFileList.add(File(file.path));
String name = file.path.split('/').last.split('.').first;
fileNames.add({0: int.parse(name), 1: file.path.split('/').last});
}
});
if (fileNames.isNotEmpty) {
final recentFile =
fileNames.reduce((curr, next) => curr[0] > next[0] ? curr : next);
String recentFileName = recentFile[1];
if (recentFileName.contains('.mp4')) {
_videoFile = File('${directory.path}/$recentFileName');
_imageFile = null;
_startVideoPlayer();
} else {
_imageFile = File('${directory.path}/$recentFileName');
_videoFile = null;
}
setState(() {});
}
}
The following async function will allow us to take a or capture an image from our camera:
Future<XFile?> takePicture() async {
final CameraController? cameraController = controller;
if (cameraController!.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
XFile file = await cameraController.takePicture();
return file;
} on CameraException catch (e) {
print('Error occured while taking picture: $e');
return null;
}
}
The function below will allow us to start a video player and play our captured or recorded video:
Future<void> _startVideoPlayer() async {
if (_videoFile != null) {
videoController = VideoPlayerController.file(_videoFile!);
await videoController!.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized,
// even before the play button has been pressed.
setState(() {});
});
await videoController!.setLooping(true);
await videoController!.play();
}
}
The following function will allow us to initiate recording of a video:
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
if (controller!.value.isRecordingVideo) {
// A recording has already started, do nothing.
return;
}
try {
await cameraController!.startVideoRecording();
setState(() {
_isRecordingInProgress = true;
print(_isRecordingInProgress);
});
} on CameraException catch (e) {
print('Error starting to record video: $e');
}
}
The following function will allow us to stop recording of a video:
Future<XFile?> stopVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// Recording is already is stopped state
return null;
}
try {
XFile file = await controller!.stopVideoRecording();
setState(() {
_isRecordingInProgress = false;
});
return file;
} on CameraException catch (e) {
print('Error stopping video recording: $e');
return null;
}
}
The following function will allow us to pause recording of a video:
Future<void> pauseVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// Video recording is not in progress
return;
}
try {
await controller!.pauseVideoRecording();
} on CameraException catch (e) {
print('Error pausing video recording: $e');
}
}
The following function will allow us to resume a previously paused video recording session:
Future<void> resumeVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// No video recording was in progress
return;
}
try {
await controller!.resumeVideoRecording();
} on CameraException catch (e) {
print('Error resuming video recording: $e');
}
}
We will also handle the lifecycle changes during Camera usage:
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
// App state changed before we got the chance to initialize.
if (cameraController == null || !cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
} else if (state == AppLifecycleState.resumed) {
onNewCameraSelected(cameraController.description);
}
}
You will find the full code in the download.
Step 4: List Captured Photos
Once you've captured your images, you may want to list them within the app. This class is responsible for that.
Create screens/captures_screen.dart and add the following imports:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_camera_demo/screens/preview_screen.dart';
Create a CapturesScreen class as a ``StatelessWidget:
class CapturesScreen extends StatelessWidget {
Define two an insatnce field and the constructor. The imageFileList will be list of image files. It will be passed via the constructor:
final List<File> imageFileList;
const CapturesScreen({required this.imageFileList});
Now build a screen with a GridView that will list the photos:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Captures',
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 2,
children: [
for (File imageFile in imageFileList)
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 2,
),
),
child: InkWell(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PreviewScreen(
fileList: imageFileList,
imageFile: imageFile,
),
),
);
},
child: Image.file(
imageFile,
fit: BoxFit.cover,
),
),
),
],
),
],
),
),
);
}
}
Step 5: Create Preview Screen
You also need to build a preview screen for our app. Create a screens/preview_screen.dart and add the following imports:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_camera_demo/screens/captures_screen.dart';
Then extend the StatelessWidget:
class PreviewScreen extends StatelessWidget {
Define two final instance fields: a File and a list of files:
final File imageFile;
final List<File> fileList;
Receive the above objects via the constructor:
const PreviewScreen({
required this.imageFile,
required this.fileList,
});
Now build the preview widget:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => CapturesScreen(
imageFileList: fileList,
),
),
);
},
child: Text('Go to all captures'),
style: TextButton.styleFrom(
primary: Colors.black,
backgroundColor: Colors.white,
),
),
),
Expanded(
child: Image.file(imageFile),
),
],
),
);
}
}
Step 6: The main class
Go to your main.dart and add the following imports:
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'screens/camera_screen.dart';
Define a list of CameraDescriptions:
List<CameraDescription> cameras = [];
Create an asynchronouse main function:
Future<void> main() async {
// Fetch the available cameras before initializing the app.
try {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
} on CameraException catch (e) {
print('Error in fetching the cameras: $e');
}
runApp(MyApp());
}
Then the MyApp class:
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: CameraScreen(),
);
}
}
Proceed below to download the full code.
Reference
| Number | Link |
|---|---|
| 1. | Download Example |
| 2. | Follow code author |