How to Pick Images from Gallery and Camera Using the Image Picker Flutter Package

Use the image_picker plugin to call ImagePicker().pickImage() with source: ImageSource.gallery or ImageSource.camera, which returns an XFile object containing the path to the selected or captured image.

To implement an image picker flutter solution, the official image_picker plugin (maintained within the flutter/flutter ecosystem) abstracts native Android and iOS APIs behind a unified Dart interface. The plugin handles platform-specific implementations—including permission requests, MIME type detection, and temporary file management—through method channels defined in image_picker.dart, allowing you to launch the camera or gallery with minimal boilerplate.

Installing the Image Picker Dependency

Add the image_picker package to your pubspec.yaml to access the core Dart API:

dependencies:
  flutter:
    sdk: flutter
  image_picker: ^0.8.7+4

After running flutter pub get, the plugin automatically bundles the platform-specific implementations (image_picker_android, image_picker_ios, etc.) that communicate with Flutter through method channels.

The primary entry point is the ImagePicker class instantiated in your widget. Calling pickImage() with the appropriate ImageSource enum value launches the corresponding native UI.

Selecting the Image Source

Pass ImageSource.gallery to open the device photo library or ImageSource.camera to launch the native camera interface:

import 'package:image_picker/image_picker.dart';

final ImagePicker _picker = ImagePicker();

// Open gallery
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);

// Launch camera
final XFile? photo = await _picker.pickImage(source: ImageSource.camera);

Complete Implementation with Error Handling

The following StatefulWidget demonstrates both sources, image constraints, and comprehensive error handling as implemented in the plugin’s architecture:

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

class ImagePickerDemo extends StatefulWidget {
  const ImagePickerDemo({Key? key}) : super(key: key);

  @override
  _ImagePickerDemoState createState() => _ImagePickerDemoState();
}

class _ImagePickerDemoState extends State<ImagePickerDemo> {
  XFile? _imageFile;
  final ImagePicker _picker = ImagePicker();

  Future<void> _pickImage(ImageSource source) async {
    try {
      final XFile? picked = await _picker.pickImage(
        source: source,
        maxWidth: 1080,
        maxHeight: 1080,
        imageQuality: 85,
      );
      // `picked` is null when the user cancels the picker.
      setState(() => _imageFile = picked);
    } on PlatformException catch (e) {
      // Handles permission denial or other platform errors.
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Error: ${e.message}')),
      );
    } catch (e) {
      // Any other unexpected errors.
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Unexpected error: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Image Picker Demo')),
      body: Column(
        children: [
          if (_imageFile != null)
            Expanded(
              child: Image.file(
                File(_imageFile!.path),
                fit: BoxFit.contain,
              ),
            )
          else
            const Expanded(
              child: Center(child: Text('No image selected')),
            ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton.icon(
                onPressed: () => _pickImage(ImageSource.gallery),
                icon: const Icon(Icons.photo),
                label: const Text('Gallery'),
              ),
              ElevatedButton.icon(
                onPressed: () => _pickImage(ImageSource.camera),
                icon: const Icon(Icons.camera_alt),
                label: const Text('Camera'),
              ),
            ],
          ),
          const SizedBox(height: 16),
        ],
      ),
    );
  }
}

Understanding the Native Implementation

When you invoke pickImage(), the plugin serializes the request into a method-channel message (pickImage) sent to the platform-specific code.

Platform-Specific Bridges

  • Android: The image_picker_android implementation receives the method call and launches an Intent for ACTION_PICK (gallery) or ACTION_IMAGE_CAPTURE (camera), handling the native activity result.
  • iOS: The image_picker_ios implementation presents a UIImagePickerController modally and manages the delegate callbacks.

After user selection, the native side copies the image to a temporary directory and returns the file path to Dart, where the plugin wraps it in an XFile object.

Configuring Platform Permissions

While the image picker flutter plugin requests runtime permissions automatically, you must declare the required permissions in your platform configuration files.

Android Permissions

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

iOS Usage Descriptions

Add to ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app requires camera access to take photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select images</string>

Processing the XFile Result

The pickImage() method returns an XFile? that is null if the user cancels the operation. When valid, the XFile provides methods to access the image data:

  • path — Absolute path to the temporary file on the device.
  • readAsBytes() — Returns a Future<List<int>> of the file contents.
  • openRead() — Returns a Stream<List<int>> for memory-efficient processing.
  • length() — Returns the file size in bytes.
if (_imageFile != null) {
  final bytes = await _imageFile!.readAsBytes();
  final size = await _imageFile!.length();
  debugPrint('Image path: ${_imageFile!.path}, Size: $size bytes');
}

Summary

  • The image picker flutter plugin abstracts native camera and gallery APIs through the ImagePicker class and pickImage() method defined in image_picker.dart.
  • Specify ImageSource.camera or ImageSource.gallery to control which native UI launches via method channels.
  • The plugin returns an XFile object containing a temporary file path, or null if the user cancels.
  • Platform implementations in image_picker_android and image_picker_ios handle native permission flows and activity results.
  • Wrap picker calls in try-catch blocks to handle PlatformException errors for permission denials.

Frequently Asked Questions

Pass the maxWidth, maxHeight, and imageQuality parameters (0-100) to pickImage(). The native implementations in image_picker_android and image_picker_ios apply these constraints before returning the file path, reducing memory usage and upload sizes.

Why does pickImage() return null instead of an XFile?

The method returns null when the user dismisses the native picker without selecting an image or capturing a photo. This is expected behavior—always check for null before updating your UI or processing the file.

Do I need to request camera and storage permissions manually in my Dart code?

No, the image_picker plugin handles runtime permission requests internally when calling pickImage(). However, you must declare the required permissions in AndroidManifest.xml and Info.plist as shown above; otherwise, the app will crash when accessing protected resources.

Can I use the image picker flutter package on desktop platforms?

Yes, the plugin supports macOS through image_picker_macos, which utilizes NSOpenPanel for gallery selection and AVCapture for camera functionality. Windows and Linux support depends on the specific implementation versions available in the Flutter plugins repository.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →