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.

camera

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.

How To Use For Loop In Flutter Widgets | Flutter Tutorial | Flutter Widgets | For Loop In Dart

How To Use For Loop In Flutter Widg...
How To Use For Loop In Flutter Widgets | Flutter Tutorial | Flutter Widgets | For Loop In Dart

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 Description and a usage description.
  • and one with the key Privacy - Microphone Usage Description and 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 {
  @override
  _CameraAppState createState() => _CameraAppState();
}

class _CameraAppState extends State<CameraApp> {
  CameraController controller;

  @override
  void initState() {
    super.initState();
    controller = CameraController(cameras[0], ResolutionPreset.max);
    controller.initialize().then((_) {
      if (!mounted) {
        return;
      }
      setState(() {});
    });
  }

  @override
  void dispose() {
    controller?.dispose();
    super.dispose();
  }

  @override
  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 {
  @override
  _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:

  @override
  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:

  @override
  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:

@override
  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 {
  @override
  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