Using Camera and Image Picker in Flutter

Flutter provides excellent support for accessing device hardware, including the camera and photo library. Integrating camera and image picker functionality allows users to capture images directly within your app or select existing images from their device. This feature is essential for various applications, such as social media platforms, e-commerce apps, and productivity tools. This blog post will guide you through implementing both camera and image picker functionalities in Flutter.

Why Integrate Camera and Image Picker in Flutter?

Integrating camera and image picker functionality can greatly enhance the user experience of your Flutter application:

  • Improved User Engagement: Allows users to directly contribute content to your app.
  • Enhanced Functionality: Enables features such as profile picture uploads, image-based reporting, and product showcasing.
  • Streamlined Workflow: Simplifies the process of adding visual content within the app.

Setting Up the Project

Before diving into the code, ensure you have a Flutter project set up and that you’re familiar with basic Flutter concepts.

Step 1: Add Dependencies

First, you need to add the image_picker package to your pubspec.yaml file. This package provides the necessary functionalities to access the device’s camera and photo library.

dependencies:
  flutter:
    sdk: flutter
  image_picker: ^0.8.4+4 # Use the latest version

After adding the dependency, run flutter pub get to install the package.

Step 2: Configure Platform-Specific Settings

Depending on the platform you are targeting (iOS or Android), you’ll need to configure certain settings to request the necessary permissions.

iOS Configuration

For iOS, you need to add permission requests to your Info.plist file located in ios/Runner/Info.plist.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>NSPhotoLibraryUsageDescription</key>
  <string>This app requires access to the photo library to select images.</string>
  <key>NSCameraUsageDescription</key>
  <string>This app requires access to the camera to take photos.</string>
  <key>NSMicrophoneUsageDescription</key>
  <string>This app needs to access your microphone for optional audio recording.</string>
</dict>
</plist>

Explanation of the keys:

  • NSPhotoLibraryUsageDescription: Message shown when the app requests access to the photo library.
  • NSCameraUsageDescription: Message shown when the app requests access to the camera.
  • NSMicrophoneUsageDescription (Optional): If you need to record audio while taking a video.
Android Configuration

For Android, permissions are requested dynamically at runtime, but you still need to declare them in the AndroidManifest.xml file located in android/app/src/main/AndroidManifest.xml.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.your_app_name">
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application
        android:label="your_app_name"
        android:icon="@mipmap/ic_launcher">
        <!-- Other elements -->
    </application>
</manifest>

Implementing Camera Functionality

Now, let’s implement the functionality to capture images using the device’s camera.

Step 1: Import the Necessary Packages

Import the required packages in your Dart file.

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

Step 2: Create the UI

Design the UI with a button to trigger the camera and display the captured image.

class CameraAndImagePickerApp extends StatefulWidget {
  @override
  _CameraAndImagePickerAppState createState() => _CameraAndImagePickerAppState();
}

class _CameraAndImagePickerAppState extends State<CameraAndImagePickerApp> {
  File? _image;
  final picker = ImagePicker();

  Future getImageFromCamera() async {
    final pickedFile = await picker.pickImage(source: ImageSource.camera);

    setState(() {
      if (pickedFile != null) {
        _image = File(pickedFile.path);
      } else {
        print('No image selected.');
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Camera & Image Picker'),
      ),
      body: Center(
        child: _image == null
            ? Text('No image selected.')
            : Image.file(_image!),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: getImageFromCamera,
        tooltip: 'Pick Image from Camera',
        child: Icon(Icons.camera),
      ),
    );
  }
}

Explanation:

  • The _CameraAndImagePickerAppState class manages the state of the UI, including the selected image (_image).
  • getImageFromCamera is an asynchronous function that uses the ImagePicker to open the camera and capture an image.
  • The setState method updates the UI with the selected image or a “No image selected” message.
  • The UI consists of a Scaffold with an AppBar, a Center widget displaying the image (if available), and a FloatingActionButton to trigger the camera.

Implementing Image Picker Functionality

Next, let’s implement the functionality to select images from the device’s photo library.

Step 1: Add a Function to Pick Images from the Gallery

Future getImageFromGallery() async {
  final pickedFile = await picker.pickImage(source: ImageSource.gallery);

  setState(() {
    if (pickedFile != null) {
      _image = File(pickedFile.path);
    } else {
      print('No image selected.');
    }
  });
}

This function is very similar to the getImageFromCamera function. The main difference is that it uses ImageSource.gallery to access the device’s photo library instead of the camera.

Step 2: Update the UI to Include a Gallery Button

Add another FloatingActionButton to trigger the image picker from the gallery.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Camera & Image Picker'),
    ),
    body: Center(
      child: _image == null
          ? Text('No image selected.')
          : Image.file(_image!),
    ),
    floatingActionButton: Column(
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        FloatingActionButton(
          onPressed: getImageFromCamera,
          tooltip: 'Pick Image from Camera',
          child: Icon(Icons.camera),
        ),
        SizedBox(height: 16),
        FloatingActionButton(
          onPressed: getImageFromGallery,
          tooltip: 'Pick Image from Gallery',
          child: Icon(Icons.photo_library),
        ),
      ],
    ),
  );
}

Handling Errors and Exceptions

When working with camera and image picker functionalities, it’s crucial to handle potential errors and exceptions gracefully. For example, the user may deny camera or storage permissions. Proper error handling ensures a better user experience.

Future getImageFromCamera() async {
  try {
    final pickedFile = await picker.pickImage(source: ImageSource.camera);

    setState(() {
      if (pickedFile != null) {
        _image = File(pickedFile.path);
      } else {
        print('No image selected.');
      }
    });
  } catch (e) {
    print('Error picking image: $e');
    // Display an error message to the user
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Text('Failed to pick image: $e'),
    ));
  }
}

Complete Code Example

Here’s the complete code example combining both camera and image picker functionalities with basic error handling.

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

class CameraAndImagePickerApp extends StatefulWidget {
  @override
  _CameraAndImagePickerAppState createState() => _CameraAndImagePickerAppState();
}

class _CameraAndImagePickerAppState extends State<CameraAndImagePickerApp> {
  File? _image;
  final picker = ImagePicker();

  Future getImageFromCamera() async {
    try {
      final pickedFile = await picker.pickImage(source: ImageSource.camera);

      setState(() {
        if (pickedFile != null) {
          _image = File(pickedFile.path);
        } else {
          print('No image selected.');
        }
      });
    } catch (e) {
      print('Error picking image: $e');
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('Failed to pick image: $e'),
      ));
    }
  }

  Future getImageFromGallery() async {
    try {
      final pickedFile = await picker.pickImage(source: ImageSource.gallery);

      setState(() {
        if (pickedFile != null) {
          _image = File(pickedFile.path);
        } else {
          print('No image selected.');
        }
      });
    } catch (e) {
      print('Error picking image: $e');
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('Failed to pick image: $e'),
      ));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Camera & Image Picker'),
      ),
      body: Center(
        child: _image == null
            ? Text('No image selected.')
            : Image.file(_image!),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: getImageFromCamera,
            tooltip: 'Pick Image from Camera',
            child: Icon(Icons.camera),
          ),
          SizedBox(height: 16),
          FloatingActionButton(
            onPressed: getImageFromGallery,
            tooltip: 'Pick Image from Gallery',
            child: Icon(Icons.photo_library),
          ),
        ],
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: CameraAndImagePickerApp(),
  ));
}

Conclusion

Integrating camera and image picker functionality in Flutter applications is straightforward with the image_picker package. By following the steps outlined in this guide, you can enable users to capture images directly within your app or select existing images from their device, greatly enhancing the user experience. Remember to handle potential errors and exceptions to ensure your application is robust and user-friendly.