Accessing the Camera & Image Picker
Accessing the Camera & Image Picker
Most mobile applications need the ability to capture photos or let users choose images from their device gallery. Flutter makes this straightforward through the official image_picker package, maintained by the Flutter team. In this lesson you will learn how to integrate image_picker, request the necessary platform permissions, pick images or capture new photos, and display the resulting file inside your widget tree.
Adding the image_picker Package
Add the dependency to pubspec.yaml and run flutter pub get:
pubspec.yaml
dependencies:
flutter:
sdk: flutter
image_picker: ^1.1.2
Platform Permission Setup
Each platform requires explicit permission declarations before your app can access hardware resources. Forgetting these is one of the most common reasons image_picker silently returns null at runtime.
- iOS — Add entries to
ios/Runner/Info.plist:NSCameraUsageDescription— shown when requesting camera accessNSPhotoLibraryUsageDescription— shown when requesting gallery accessNSPhotoLibraryAddUsageDescription— required if you save back to the gallery
- Android — For Android 13+ (API 33+) declare
READ_MEDIA_IMAGESinAndroidManifest.xml. For older versionsREAD_EXTERNAL_STORAGEis used automatically by the plugin.
NSPhotoLibraryUsageDescription or NSCameraUsageDescription keys are missing from Info.plist, your app will crash when it attempts to present the picker. Always provide a clear, human-readable reason string — Apple reviewers reject apps with vague descriptions.Picking an Image from the Gallery
The ImagePicker class exposes an pickImage method that returns an XFile? (a platform-independent file handle). The method is asynchronous and returns null if the user cancels.
Gallery Picker Example
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class GalleryPickerDemo extends StatefulWidget {
const GalleryPickerDemo({super.key});
@override
State<GalleryPickerDemo> createState() => _GalleryPickerDemoState();
}
class _GalleryPickerDemoState extends State<GalleryPickerDemo> {
File? _selectedImage;
final ImagePicker _picker = ImagePicker();
Future<void> _pickFromGallery() async {
final XFile? file = await _picker.pickImage(
source: ImageSource.gallery,
imageQuality: 85, // compress to 85% quality
maxWidth: 1024, // cap width to reduce file size
);
if (file != null) {
setState(() {
_selectedImage = File(file.path);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Image Picker')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_selectedImage != null)
Image.file(_selectedImage!, height: 250, fit: BoxFit.cover)
else
const Text('No image selected.'),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: _pickFromGallery,
icon: const Icon(Icons.photo_library),
label: const Text('Pick from Gallery'),
),
],
),
),
);
}
}
XFile is a cross-platform file abstraction from the cross_file package (re-exported by image_picker). Convert it to a dart:io File with File(xfile.path) when you need file-system APIs, such as uploading via http.MultipartFile.Capturing a Photo with the Camera
Switching from the gallery to the live camera requires only one change: pass ImageSource.camera. All other parameters — quality, max dimensions, preferred camera (front or rear) — remain the same.
Camera Capture Example
Future<void> _captureFromCamera() async {
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.rear,
imageQuality: 90,
);
if (photo != null) {
setState(() {
_selectedImage = File(photo.path);
});
}
}
// In your build method, add a second button:
ElevatedButton.icon(
onPressed: _captureFromCamera,
icon: const Icon(Icons.camera_alt),
label: const Text('Open Camera'),
),
Handling Permissions Gracefully
On Android 13+ and on iOS, the system may deny permission either permanently or for the session. The image_picker plugin handles the initial permission request automatically, but you should handle the null return carefully and consider prompting the user to open app settings when access is permanently denied.
- If
pickImagereturnsnull, the user cancelled — do nothing. - If the permission is denied, some versions of the plugin throw a
PlatformException— wrap the call in a try-catch. - Use the permission_handler package alongside image_picker when you need fine-grained permission status checks.
imageQuality (e.g. 80–90) and maxWidth/maxHeight constraints when picking from the gallery. Un-constrained images from modern smartphones can be 10–20 MB, which will slow upload and bloat memory usage inside Image.file.Picking Videos
The same ImagePicker instance exposes pickVideo and pickMultiImage for selecting multiple photos at once. The return types mirror those of pickImage:
pickVideo(source: ImageSource.gallery)— returnsXFile?pickMultiImage()— returnsList<XFile>(never null, may be empty)
Summary
The image_picker package provides a high-level, permission-aware API for the two most common media-access scenarios on mobile: selecting from the gallery and capturing with the camera. The workflow is consistent: instantiate ImagePicker, await pickImage(source: ...), check for null, convert to File if needed, and call setState to update the UI. Robust apps always cap image dimensions, handle cancellation, and guide users to app settings when permission is permanently denied.