Flutter makes it easy to access device cameras, capture photos, record videos, and select media from the gallery. In this tutorial, you'll build a complete Flutter media application that combines camera functionality and image/video selection using the latest Flutter packages.
By the end of this tutorial, you'll learn how to:
- Capture photos using the device camera
- Record videos
- Pick images from the gallery
- Pick videos from the gallery
- Display media previews
- Handle permissions properly
- Save and manage captured files
What We'll Build
The application will include:
- Take Photo button
- Record Video button
- Pick an image from the gallery
- Pick a video from the gallery
- Media Preview Screen
- Responsive Material 3 UI
Prerequisites
Before starting, ensure you have:
- Flutter SDK 3.35+
- Dart 3.9+
- Android Studio or VS Code
- Android Emulator or Physical Device
- Xcode (for iOS development)
Step 1. Create a New Flutter Project
Create a new Flutter application.
flutter create flutter_camera_image_picker
cd flutter_camera_image_picker
Open the project in your favorite editor.
code .
Step 2. Add Required Dependencies
Open pubspec.yaml and add the following packages:
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
camera: ^0.11.2+1
image_picker: ^1.2.0
path_provider: ^2.1.5
path: ^1.9.1
Install the packages:
flutter pub get
Package Overview
| Package | Purpose |
|---|---|
| camera | Camera preview, photo capture, video recording |
| image_picker | Select images and videos from the gallery |
| path_provider | Access application storage |
| path | File path manipulation |
Step 3. Configure Android Permissions
Open:
android/app/src/main/AndroidManifest.xml
Add permissions before the <application> tag:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Inside the <application> tag add:
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
Step 4. Configure iOS Permissions
Open:
ios/Runner/Info.plist
Add:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos and record videos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for video recording.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to your photo library.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app saves photos and videos to your library.</string>
Step 5. Create the Media Service
Create a new file:
lib/services/media_service.dart
Add the following code:
import 'package:camera/camera.dart';
import 'package:image_picker/image_picker.dart';
class MediaService {
final ImagePicker _picker = ImagePicker();
Future<XFile?> pickImage() async {
return await _picker.pickImage(
source: ImageSource.gallery,
imageQuality: 90,
);
}
Future<XFile?> pickVideo() async {
return await _picker.pickVideo(
source: ImageSource.gallery,
);
}
}
This service handles media selection from the gallery.
Step 6. Create the Camera Screen
Replace the contents of lib/main.dart.
Import Dependencies
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final cameras = await availableCameras();
runApp(const MyApp(cameras: cameras));
}
Create Application Root
class MyApp extends StatelessWidget {
final List<CameraDescription> cameras;
const MyApp({
super.key,
required this.cameras,
});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Media App',
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: CameraHomePage(cameras: cameras),
);
}
}
Create Camera Home Page
class CameraHomePage extends StatefulWidget {
final List<CameraDescription> cameras;
const CameraHomePage({
super.key,
required this.cameras,
});
@override
State<CameraHomePage> createState() => _CameraHomePageState();
}
State Variables
class _CameraHomePageState extends State<CameraHomePage> {
late CameraController _controller;
bool _isInitialized = false;
bool _isRecording = false;
XFile? _selectedImage;
XFile? _selectedVideo;
Initialize Camera
@override
void initState() {
super.initState();
_initializeCamera();
}
Future<void> _initializeCamera() async {
_controller = CameraController(
widget.cameras.first,
ResolutionPreset.high,
);
await _controller.initialize();
if (mounted) {
setState(() {
_isInitialized = true;
});
}
}
Capture Photo
Future<void> _takePhoto() async {
if (!_controller.value.isInitialized) return;
final photo = await _controller.takePicture();
setState(() {
_selectedImage = photo;
_selectedVideo = null;
});
}
Record Video
Future<void> _recordVideo() async {
if (_isRecording) {
final video = await _controller.stopVideoRecording();
setState(() {
_selectedVideo = video;
_selectedImage = null;
_isRecording = false;
});
} else {
await _controller.startVideoRecording();
setState(() {
_isRecording = true;
});
}
}
Pick Image
Future<void> _pickImage() async {
final picker = ImagePicker();
final image = await picker.pickImage(
source: ImageSource.gallery,
imageQuality: 90,
);
if (image != null) {
setState(() {
_selectedImage = image;
_selectedVideo = null;
});
}
}
Pick Video
Future<void> _pickVideo() async {
final picker = ImagePicker();
final video = await picker.pickVideo(
source: ImageSource.gallery,
);
if (video != null) {
setState(() {
_selectedVideo = video;
_selectedImage = null;
});
}
}
Dispose Resources
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Build UI
@override
Widget build(BuildContext context) {
if (!_isInitialized) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Camera App'),
),
body: SingleChildScrollView(
child: Column(
children: [
AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: CameraPreview(_controller),
),
const SizedBox(height: 16),
Wrap(
spacing: 10,
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
FilledButton.icon(
onPressed: _takePhoto,
icon: const Icon(Icons.camera_alt),
label: const Text('Take Photo'),
),
FilledButton.icon(
onPressed: _recordVideo,
icon: Icon(
_isRecording
? Icons.stop
: Icons.videocam,
),
label: Text(
_isRecording
? 'Stop Recording'
: 'Record Video',
),
),
OutlinedButton.icon(
onPressed: _pickImage,
icon: const Icon(Icons.image),
label: const Text('Gallery Image'),
),
OutlinedButton.icon(
onPressed: _pickVideo,
icon: const Icon(Icons.video_library),
label: const Text('Gallery Video'),
),
],
),
const SizedBox(height: 24),
if (_selectedImage != null)
Column(
children: [
const Text(
'Selected Image',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Image.file(
File(_selectedImage!.path),
height: 300,
),
],
),
if (_selectedVideo != null)
Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Icon(
Icons.video_file,
size: 80,
),
const SizedBox(height: 10),
Text(
_selectedVideo!.name,
textAlign: TextAlign.center,
),
],
),
),
],
),
),
);
}
}
Step 7. Run the Application
Run the application:
flutter run
You should see:
- Live camera preview
- Photo capture functionality
- Video recording functionality
- Gallery image picker
- Gallery video picker
- Media preview area
Understanding the Camera Workflow
The Flutter camera package works in three steps:
1. Get Available Cameras
final cameras = await availableCameras();
2. Create Controller
CameraController(
camera,
ResolutionPreset.high,
);
3. Initialize Camera
await controller.initialize();
Only after initialization can the preview and capture functions be used.
Adding Front Camera Support
To switch between front and rear cameras:
int _cameraIndex = 0;
Add:
Future<void> switchCamera() async {
_cameraIndex =
(_cameraIndex + 1) %
widget.cameras.length;
await _controller.dispose();
_controller = CameraController(
widget.cameras[_cameraIndex],
ResolutionPreset.high,
);
await _controller.initialize();
setState(() {});
}
Add a button:
IconButton(
onPressed: switchCamera,
icon: const Icon(Icons.flip_camera_ios),
)
Improving User Experience
For production applications, consider:
Image Compression
await picker.pickImage(
imageQuality: 80,
);
Limit Video Duration
await picker.pickVideo(
source: ImageSource.gallery,
maxDuration: Duration(minutes: 2),
);
Save Files to App Storage
Use path_provider:
final directory =
await getApplicationDocumentsDirectory();
Display Video Preview
Add:
video_player: ^2.10.0
This allows users to play recorded videos directly inside the app.
Common Issues and Solutions
Camera Preview Black Screen
Make sure:
await controller.initialize();
has completed before rendering the preview.
Permission Denied
Verify Android and iOS permission configurations are correct.
Emulator Camera Not Working
Use a physical device whenever possible. Many emulators provide limited camera support.
Video Recording Fails
Ensure microphone permission is granted:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
>Conclusion
In this tutorial, you've built a complete Flutter photo and video application using the latest Flutter ecosystem. You learned how to integrate the camera package for live camera previews, photo capture, and video recording, while also leveraging image_picker to select media from the device gallery.
You also explored permission handling, media previews, camera initialization, front/rear camera switching, and several production-ready enhancements. These techniques provide a solid foundation for building social media apps, profile image upload systems, messaging applications, content creation tools, and any Flutter app that requires rich media functionality.
From here, you can extend the project by adding video playback, image editing, cloud uploads, offline storage, AI-powered image analysis, or integration with Firebase Storage and Supabase Storage to create a fully featured media management application.
You can find the full source code on our GitHub.
We know that building beautifully designed Mobile and Web Apps from scratch can be frustrating and very time-consuming. Check Envato unlimited downloads and save development and design time.
That's just the basics. If you need more deep learning about Flutter and Dart, you can take the following cheap course:
- Flutter & Dart - The Complete Guide [2025 Edition]
- The Complete Flutter Development Bootcamp with Dart
- Complete Flutter Guide 2025: Build Android, iOS and Web apps
- Advanced Flutter: Build Enterprise-Ready Apps.
- Flutter , Nodejs, Express , MongoDB: Build Multi-Store App
- Flutter & Dart Essentials-Build Mobile Apps like a Pro
- Master Flutter with ML: Object Detection, OCR & Beyond
- Full-Stack Mobile Development: Flutter, Figma, and Firebase
- Dart & Flutter - Zero to Mastery [2025] + Clean Architecture
- Master Flame Game Engine with Flutter: Build 2D Mobile Games
Thanks!
