Photo Editing Application in Flutter
Last Updated :
09 Apr, 2025
With the introduction of the powerful image_editor_plus package, editing images directly within your Flutter app has become significantly more accessible. In this article, we'll dive deep into building a user-friendly image editor app. Users will have the ability to select an image from their camera or gallery, allowing for versatility based on their preferences. Once an image is chosen, they can utilize the built-in UI to explore a range of editing features, such as cropping, adjusting brightness, applying filters, and more. Finally, users will be able to preview and save their edited images, providing a complete and satisfying experience from start to finish.
Steps to Create a Photo-Editing Application in Flutter
Step 1 : Create a New Flutter Application
Create a new Flutter application using the command Prompt. To create a new app, write the below command and run it.
flutter create app_name
To know more about it refer this article: Creating a Simple Application in Flutter
Step 2 : Adding the Dependency
Packages Used: We’re using the following Flutter packages:
Package | Purpose |
---|
image_picker | To pick images from the camera or gallery |
---|
image_editor_plus | Provides a full-featured editor UI |
---|
path_provider | To save edited images in a temporary folder |
---|
permission_handler | Handles runtime permissions |
---|
To add the dependency to the pubspec.yaml file, add image_picker, image_editor_plus, path_provider, and permission_handler as a dependency in the dependencies part of the pubspec.yaml file, as shown below:
Dart
dependencies:
flutter:
sdk: flutter
image_editor_plus: ^1.0.6
image_picker: ^1.1.2
path_provider: ^2.1.5
permission_handler: ^11.4.0
Now run the below command in the terminal.
flutter pub get
Or
Run the below command in the terminal.
flutter pub add image_picker image_editor_plus path_provider permission_handler
Step 3 : Import dependencies
To use libraries, import all of them in the main.dart file,
import 'package:image_picker/image_picker.dart';
import 'package:image_editor_plus/image_editor_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
Step 4 : Working With main.dart
Add the boilerplate code below in main.dart to create a basic structure with an AppBar and body using a Scaffold.
Dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_editor_plus/image_editor_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
// Entry point of the application
void main() {
runApp(const MyApp());
}
// Root widget of the application
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PhotoEditorApp(),
// Disable debug banner
debugShowCheckedModeBanner: false,
);
}
}
// Stateful widget for the photo editor app
class PhotoEditorApp extends StatefulWidget {
const PhotoEditorApp({Key? key}) : super(key: key);
@override
State<PhotoEditorApp> createState() => _PhotoEditorAppState();
}
class _PhotoEditorAppState extends State<PhotoEditorApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Image Editor Plus"),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
body: Center(
child: // Code Here
),
);
}
}
Step 5 : Initialize variables
Intialize required variables.
Dart
// Holds the edited image file
File? _editedImage;
Step 6 : Create methods
Create a method to pick an image from the specified source and edit it using the image_picker and image_editor_plus packages.
Dart
// Function to pick an image from the specified source and edit it
Future<void> _pickAndEditImage(ImageSource source) async {
// Request necessary permissions
await Permission.photos.request();
await Permission.camera.request();
await Permission.storage.request();
// Pick an image using the ImagePicker
final pickedFile = await ImagePicker().pickImage(source: source);
// If no image is selected, return
if (pickedFile == null) return;
final imageFile = File(pickedFile.path);
// Open the image editor
final editedImage = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageEditor(image: imageFile.readAsBytesSync()),
),
);
// If an edited image is returned, save it to a temporary directory
if (editedImage != null) {
final tempDir = await getTemporaryDirectory();
final filePath = '${tempDir.path}/edited_${DateTime.now().millisecondsSinceEpoch}.png';
final file = File(filePath);
await file.writeAsBytes(editedImage);
// Update the state with the edited image
setState(() {
_editedImage = file;
});
// Show a success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Image Edited Successfully')),
);
}
}
Step 7 : Develop UI
- ElevatedButton : Used to select images from the camera and gallery.
Dart
// Button to edit an image from the camera
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
// Button background color
backgroundColor: Colors.green,
// Button text/icon color
foregroundColor: Colors.white,
),
// Camera icon
icon: const Icon(Icons.camera_alt),
// Button label
label: const Text("Edit from Camera"),
onPressed: () => _pickAndEditImage(ImageSource.camera),
),
// Button to edit an image from the gallery
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
// Button background color
backgroundColor: Colors.green,
// Button text/icon color
foregroundColor: Colors.white,
),
// Gallery icon
icon: const Icon(Icons.photo_library),
// Button label
label: const Text("Edit from Gallery"),
onPressed: () => _pickAndEditImage(ImageSource.gallery),
),
- Image.file() : Display the edited image if available.
Dart
// Display the edited image if available
if (_editedImage != null) Image.file(_editedImage!, height: 300),
- Column : It is used to arrange its child widgets like Image.file() and two ElevatedButtons vertically in a single column.
Dart
Column(
// Center the content
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Display the edited image if available
if (_editedImage != null) Image.file(_editedImage!, height: 300),
// Add spacing
const SizedBox(height: 30),
// Button to edit an image from the camera
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
// Camera icon
icon: const Icon(Icons.camera_alt),
// Button label
label: const Text("Edit from Camera"),
onPressed: () => _pickAndEditImage(ImageSource.camera),
),
// Add spacing
const SizedBox(height: 10),
// Button to edit an image from the gallery
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
// Gallery icon
icon: const Icon(Icons.photo_library),
// Button label
label: const Text("Edit from Gallery"),
onPressed: () => _pickAndEditImage(ImageSource.gallery),
),
],
),
Complete Source Code
main.dart:
Dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_editor_plus/image_editor_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
// Entry point of the application
void main() {
runApp(const MyApp());
}
// Root widget of the application
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PhotoEditorApp(),
// Disable debug banner
debugShowCheckedModeBanner: false,
);
}
}
// Stateful widget for the photo editor app
class PhotoEditorApp extends StatefulWidget {
const PhotoEditorApp({Key? key}) : super(key: key);
@override
State<PhotoEditorApp> createState() => _PhotoEditorAppState();
}
class _PhotoEditorAppState extends State<PhotoEditorApp> {
// Holds the edited image file
File? _editedImage;
// Function to pick an image from
// the specified source and edit it
Future<void> _pickAndEditImage(ImageSource source) async {
// Request necessary permissions
await Permission.photos.request();
await Permission.camera.request();
await Permission.storage.request();
// Pick an image using the ImagePicker
final pickedFile = await ImagePicker().pickImage(source: source);
// If no image is selected, return
if (pickedFile == null) return;
final imageFile = File(pickedFile.path);
// Open the image editor
final editedImage = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageEditor(image: imageFile.readAsBytesSync()),
),
);
// If an edited image is returned, save it to a temporary directory
if (editedImage != null) {
final tempDir = await getTemporaryDirectory();
final filePath = '${tempDir.path}/edited_${DateTime.now().millisecondsSinceEpoch}.png';
final file = File(filePath);
await file.writeAsBytes(editedImage);
// Update the state with the edited image
setState(() {
_editedImage = file;
});
// Show a success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Image Edited Successfully')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// App bar title
title: const Text("Image Editor Plus"),
// App bar background color
backgroundColor: Colors.green,
// App bar text color
foregroundColor: Colors.white,
),
body: Center(
child: Column(
// Center the content
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Display the edited image if available
if (_editedImage != null) Image.file(_editedImage!, height: 300),
// Add spacing
const SizedBox(height: 30),
// Button to edit an image from the camera
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
// Button background color
backgroundColor: Colors.green,
// Button text/icon color
foregroundColor: Colors.white,
),
// Camera icon
icon: const Icon(Icons.camera_alt),
// Button label
label: const Text("Edit from Camera"),
onPressed: () => _pickAndEditImage(ImageSource.camera),
),
// Add spacing
const SizedBox(height: 10),
// Button to edit an image from the gallery
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
// Button background color
backgroundColor: Colors.green,
// Button text/icon color
foregroundColor: Colors.white,
),
// Gallery icon
icon: const Icon(Icons.photo_library),
// Button label
label: const Text("Edit from Gallery"),
onPressed: () => _pickAndEditImage(ImageSource.gallery),
),
],
),
),
);
}
}
Output: