The ProImageEditor is a Flutter widget designed for image editing within your application. It provides a flexible and convenient way to integrate image editing capabilities into your Flutter project.
Table of contents
- π· Preview
- β¨ Features
- π§ Getting started
- β Usage
- π Documentation
- π€ Contributing
- π License
- π Notices
Preview
Grounded-Design | Frosted-Glass-Design |
---|---|
WhatsApp-Design | Paint-Editor |
---|---|
Text-Editor | Crop-Rotate-Editor |
---|---|
Filter-Editor | Emoji-Editor |
---|---|
Sticker/ Widget Editor | Blur-Editor |
---|---|
Features
- β
Multiple-Editors
- β
Paint-Editor
- β Color picker
- β Multiple forms like arrow, rectangle, circle and freestyle
- β
Text-Editor
- β Color picker
- β Align-Text => left, right and center
- β Change Text Scale
- β Multiple background modes like in whatsapp
- β
Crop-Rotate-Editor
- β Rotate
- β Flip
- β Multiple aspect ratios
- β Reset
- β Double-Tap
- β Round cropper
- β Filter-Editor
- β Blur-Editor
- β Emoji-Picker
- β Sticker-Editor
- β
Paint-Editor
- β
Multi-Threading
- β Use isolates for background tasks on Dart native devices
- β Use web-workers for background tasks on Dart web devices
- β Automatically set the number of active background processors based on the device
- β Manually set the number of active background processors
- β Undo and redo function
- β Use your image directly from memory, asset, file or network
- β Each icon can be changed
- β Any text can be translated "i18n"
- β Many custom configurations for each subeditor
- β Custom theme for each editor
- β Selectable design mode between Material and Cupertino
- β Reorder layer level
- β Movable background image
- β WhatsApp Theme
- β Frosted-Glass Theme
- β Interactive layers
- β Helper lines for better positioning
- β Hit detection for painted layers
- β Zoomable paint and main editor
- β Improved layer movement and scaling functionality for desktop devices
Planned features
- β¨ Paint-Editor
- New mode which pixelates the background
- Freestyle-Painter with improved performance and hitbox
- β¨ Text-Editor
- Text-layer with an improved hit-box and ensure it's vertically centered on all devices
- β¨ Emoji-Editor
- Preload emojis in web platforms
- β¨ AI Futures => Perhaps integrating Adobe Firefly
Getting started
Android
To enable smooth hit vibrations from a helper line, you need to add the VIBRATE
permission to your AndroidManifest.xml
file.
<uses-permission android:name="android.permission.VIBRATE"/>
OpenHarmony
To enable smooth hit vibrations from a helper line, you need to add the VIBRATE
permission to your project's module.json5 file.
"requestPermissions": [
{"name" : "ohos.permission.VIBRATE"},
]
Web
If you're displaying emoji on the web and want them to be colored by default (especially if you're not using a custom font like Noto Emoji), you can achieve this by adding the useColorEmoji: true
parameter to your flutter_bootstrap.js
file, as shown in the code snippet below:
Show code example
{{flutter_js}}
{{flutter_build_config}}
_flutter.loader.load({
serviceWorkerSettings: {
serviceWorkerVersion: {{flutter_service_worker_version}},
},
onEntrypointLoaded: function (engineInitializer) {
engineInitializer.initializeEngine({
useColorEmoji: true, // add this parameter
renderer: 'canvaskit'
}).then(function (appRunner) {
appRunner.runApp();
});
}
});
The HTML renderer can cause problems on some devices, especially mobile devices. If you don't know the exact type of phone your customers will be using, it is recommended to use the Canvas renderer.
To enable the Canvaskit renderer by default for better compatibility with mobile web devices, you can do the following in your flutter_bootstrap.js
file.
Show code example
{{flutter_js}}
{{flutter_build_config}}
_flutter.loader.load({
serviceWorkerSettings: {
serviceWorkerVersion: {{flutter_service_worker_version}},
},
onEntrypointLoaded: function (engineInitializer) {
engineInitializer.initializeEngine({
useColorEmoji: true,
renderer: 'canvaskit' // add this parameter
}).then(function (appRunner) {
appRunner.runApp();
});
}
});
By making this change, you can enhance filter compatibility and ensure a smoother experience on older Android phones and various mobile web devices.
You can view the full web example here.
iOS, macOS, Linux, Windows
No further action is required.
Usage
Import first the image editor like below:
import 'package:pro_image_editor/pro_image_editor.dart';
Open the editor in a new page
void _openEditor() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProImageEditor.network(
'https://picsum.photos/id/237/2000',
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (Uint8List bytes) async {
/*
Your code to handle the edited image. Upload it to your server as an example.
You can choose to use await, so that the loading-dialog remains visible until your code is ready, or no async, so that the loading-dialog closes immediately.
By default, the bytes are in `jpg` format.
*/
Navigator.pop(context);
},
),
),
),
);
}
Show the editor inside of a widget
@override
Widget build(BuildContext context) {
return Scaffold(
body: ProImageEditor.network(
'https://picsum.photos/id/237/2000',
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (Uint8List bytes) async {
/*
Your code to handle the edited image. Upload it to your server as an example.
You can choose to use await, so that the loading-dialog remains visible until your code is ready, or no async, so that the loading-dialog closes immediately.
By default, the bytes are in `jpg` format.
*/
Navigator.pop(context);
},
),
),
);
}
Own stickers or widgets
To display stickers or widgets in the ProImageEditor, you have the flexibility to customize and load your own content. The buildStickers
method allows you to define your own logic for loading stickers, whether from a backend, assets, or local storage, and then push them into the editor. The example here demonstrates how to load images that can serve as stickers and then add them to the editor.
Frosted-Glass design
To use the "Frosted-Glass-Design" you can follow the example here
WhatsApp design
The image editor offers a WhatsApp-themed option that mirrors the popular messaging app's design. The editor also follows the small changes that exist in the Material (Android) and Cupertino (iOS) version.
You can see the complete example here
Highly configurable
Customize the image editor to suit your preferences. Of course, each class like I18nTextEditor
includes more configuration options.
Show code example
return Scaffold(
appBar: AppBar(
title: const Text('Pro-Image-Editor')
),
body: ProImageEditor.network(
'https://picsum.photos/id/237/2000',
key: _editor,
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (Uint8List bytes) async {
/*
Your code to handle the edited image. Upload it to your server as an example.
You can choose to use await, so that the loading-dialog remains visible until your code is ready, or no async, so that the loading-dialog closes immediately.
By default, the bytes are in `jpg` format.
*/
Navigator.pop(context);
},
),
configs: ProImageEditorConfigs(
activePreferredOrientations: [
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
],
i18n: const I18n(
various: I18nVarious(),
paintEditor: I18nPaintEditor(),
textEditor: I18nTextEditor(),
cropRotateEditor: I18nCropRotateEditor(),
filterEditor: I18nFilterEditor(filters: I18nFilters()),
emojiEditor: I18nEmojiEditor(),
stickerEditor: I18nStickerEditor(),
// More translations...
),
helperLines: const HelperLines(
showVerticalLine: true,
showHorizontalLine: true,
showRotateLine: true,
hitVibration: true,
),
customWidgets: const ProImageEditorCustomWidgets(),
imageEditorTheme: const ImageEditorTheme(
layerHoverCursor: SystemMouseCursors.move,
helperLine: HelperLineTheme(
horizontalColor: Color(0xFF1565C0),
verticalColor: Color(0xFF1565C0),
rotateColor: Color(0xFFE91E63),
),
paintEditor: PaintEditorTheme(),
textEditor: TextEditorTheme(),
cropRotateEditor: CropRotateEditorTheme(),
filterEditor: FilterEditorTheme(),
emojiEditor: EmojiEditorTheme(),
stickerEditor: StickerEditorTheme(),
background: Color.fromARGB(255, 22, 22, 22),
loadingDialogTextColor: Color(0xFFE1E1E1),
uiOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Color(0x42000000),
statusBarIconBrightness: Brightness.light,
systemNavigationBarIconBrightness: Brightness.light,
statusBarBrightness: Brightness.dark,
systemNavigationBarColor: Color(0xFF000000),
),
),
icons: const ImageEditorIcons(
paintEditor: IconsPaintEditor(),
textEditor: IconsTextEditor(),
cropRotateEditor: IconsCropRotateEditor(),
filterEditor: IconsFilterEditor(),
emojiEditor: IconsEmojiEditor(),
stickerEditor: IconsStickerEditor(),
closeEditor: Icons.clear,
doneIcon: Icons.done,
applyChanges: Icons.done,
backButton: Icons.arrow_back,
undoAction: Icons.undo,
redoAction: Icons.redo,
removeElementZone: Icons.delete_outline_rounded,
),
paintEditorConfigs: const PaintEditorConfigs(),
textEditorConfigs: const TextEditorConfigs(),
cropRotateEditorConfigs: const CropRotateEditorConfigs(),
filterEditorConfigs: FilterEditorConfigs(),
emojiEditorConfigs: const EmojiEditorConfigs(),
stickerEditorConfigs: StickerEditorConfigs(
enabled: true,
buildStickers: (setLayer) {
return ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
child: Container(
color: const Color.fromARGB(255, 224, 239, 251),
child: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: 21,
shrinkWrap: true,
itemBuilder: (context, index) {
Widget widget = ClipRRect(
borderRadius: BorderRadius.circular(7),
child: Image.network(
'https://picsum.photos/id/${(index + 3) * 3}/2000',
width: 120,
height: 120,
fit: BoxFit.cover,
),
);
return GestureDetector(
onTap: () => setLayer(widget),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: widget,
),
);
},
),
),
);
},
),
designMode: ImageEditorDesignModeE.material,
heroTag: 'hero',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue.shade800,
brightness: Brightness.dark,
),
),
),
)
);
Custom AppBar
Customize the AppBar with your own widgets. The same is also possible with the BottomBar.
Show code example
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:pro_image_editor/pro_image_editor.dart';
class Demo extends StatefulWidget {
const Demo({super.key});
@override
State<Demo> createState() => DemoState();
}
class DemoState extends State<Demo> {
final _editorKey = GlobalKey<ProImageEditorState>();
late StreamController _updateAppBarStream;
@override
void initState() {
_updateAppBarStream = StreamController.broadcast();
super.initState();
}
@override
void dispose() {
_updateAppBarStream.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ProImageEditor.network(
'https://picsum.photos/id/237/2000',
key: _editorKey,
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (Uint8List bytes) async {
/*
Your code to handle the edited image. Upload it to your server as an example.
You can choose to use await, so that the loading-dialog remains visible until your code is ready, or no async, so that the loading-dialog closes immediately.
By default, the bytes are in `jpg` format.
*/
Navigator.pop(context);
},
onUpdateUI: () {
_updateAppBarStream.add(null);
},
),
configs: ProImageEditorConfigs(
customWidgets: ImageEditorCustomWidgets(
appBar: AppBar(
automaticallyImplyLeading: false,
foregroundColor: Colors.white,
backgroundColor: Colors.black,
actions: [
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Cancel',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.close),
onPressed: _editorKey.currentState?.closeEditor,
);
}),
const Spacer(),
IconButton(
tooltip: 'Custom Icon',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(
Icons.bug_report,
color: Colors.white,
),
onPressed: () {},
),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Undo',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
Icons.undo,
color: _editorKey.currentState?.canUndo == true ? Colors.white : Colors.white.withAlpha(80),
),
onPressed: _editorKey.currentState?.undoAction,
);
},
),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Redo',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
Icons.redo,
color: _editorKey.currentState?.canRedo == true ? Colors.white : Colors.white.withAlpha(80),
),
onPressed: _editorKey.currentState?.redoAction,
);
},
),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Done',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.done),
iconSize: 28,
onPressed: _editorKey.currentState?.doneEditing,
);
}),
],
),
appBarPaintEditor: AppBar(
automaticallyImplyLeading: false,
foregroundColor: Colors.white,
backgroundColor: Colors.black,
actions: [
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.arrow_back),
onPressed: _editorKey.currentState?.paintEditor.currentState?.close,
);
}),
const SizedBox(width: 80),
const Spacer(),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(
Icons.line_weight_rounded,
color: Colors.white,
),
onPressed: _editorKey.currentState?.paintEditor.currentState?.openLineWeightBottomSheet,
);
}),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
_editorKey.currentState?.paintEditor.currentState?.fillBackground == true
? Icons.format_color_reset
: Icons.format_color_fill,
color: Colors.white,
),
onPressed: _editorKey.currentState?.paintEditor.currentState?.toggleFill);
}),
const Spacer(),
IconButton(
tooltip: 'Custom Icon',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(
Icons.bug_report,
color: Colors.white,
),
onPressed: () {},
),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Undo',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
Icons.undo,
color: _editorKey.currentState?.paintEditor.currentState?.canUndo == true ? Colors.white : Colors.white.withAlpha(80),
),
onPressed: _editorKey.currentState?.paintEditor.currentState?.undoAction,
);
}),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Redo',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
Icons.redo,
color: _editorKey.currentState?.paintEditor.currentState?.canRedo == true ? Colors.white : Colors.white.withAlpha(80),
),
onPressed: _editorKey.currentState?.paintEditor.currentState?.redoAction,
);
}),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Done',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.done),
iconSize: 28,
onPressed: _editorKey.currentState?.paintEditor.currentState?.done,
);
}),
],
),
appBarTextEditor: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.black,
foregroundColor: Colors.white,
actions: [
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.arrow_back),
onPressed: _editorKey.currentState?.textEditor.currentState?.close,
);
}),
const Spacer(),
IconButton(
tooltip: 'Custom Icon',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(
Icons.bug_report,
color: Colors.white,
),
onPressed: () {},
),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
onPressed: _editorKey.currentState?.textEditor.currentState?.toggleTextAlign,
icon: Icon(
_editorKey.currentState?.textEditor.currentState?.align == TextAlign.left
? Icons.align_horizontal_left_rounded
: _editorKey.currentState?.textEditor.currentState?.align == TextAlign.right
? Icons.align_horizontal_right_rounded
: Icons.align_horizontal_center_rounded,
),
);
}),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
onPressed: _editorKey.currentState?.textEditor.currentState?.toggleBackgroundMode,
icon: const Icon(Icons.layers_rounded),
);
}),
const Spacer(),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.done),
iconSize: 28,
onPressed: _editorKey.currentState?.textEditor.currentState?.done,
);
}),
],
),
appBarCropRotateEditor: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.black,
foregroundColor: Colors.white,
actions: [
StreamBuilder(
stream: _updateUIStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.arrow_back),
onPressed: editorKey.currentState?.cropRotateEditor.currentState?.close,
);
}),
const Spacer(),
IconButton(
tooltip: 'My Button',
color: Colors.amber,
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(
Icons.bug_report,
color: Colors.amber,
),
onPressed: () {},
),
StreamBuilder(
stream: _updateUIStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Undo',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
Icons.undo,
color: editorKey.currentState!.cropRotateEditor.currentState!.canUndo ? Colors.white : Colors.white.withAlpha(80),
),
onPressed: editorKey.currentState!.cropRotateEditor.currentState!.undoAction,
);
}),
StreamBuilder(
stream: _updateUIStream.stream,
builder: (_, __) {
return IconButton(
tooltip: 'Redo',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: Icon(
Icons.redo,
color: editorKey.currentState!.cropRotateEditor.currentState!.canRedo ? Colors.white : Colors.white.withAlpha(80),
),
onPressed: editorKey.currentState!.cropRotateEditor.currentState!.redoAction,
);
}),
StreamBuilder(
stream: _updateUIStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.done),
iconSize: 28,
onPressed: editorKey.currentState!.cropRotateEditor.currentState!.done,
);
}),
],
),
appBarFilterEditor: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.black,
foregroundColor: Colors.white,
actions: [
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.arrow_back),
onPressed: _editorKey.currentState?.filterEditor.currentState?.close,
);
}),
const Spacer(),
IconButton(
tooltip: 'Custom Icon',
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(
Icons.bug_report,
color: Colors.white,
),
onPressed: () {},
),
StreamBuilder(
stream: _updateAppBarStream.stream,
builder: (_, __) {
return IconButton(
padding: const EdgeInsets.symmetric(horizontal: 8),
icon: const Icon(Icons.done),
iconSize: 28,
onPressed: _editorKey.currentState?.filterEditor.currentState?.done,
);
}),
],
),
),
),
);
}
}
Upload to Firebase or Supabase
Firebase example
ProImageEditor.asset(
'assets/demo.png',
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (bytes) async {
try {
String path = 'your-storage-path/my-image-name.jpg';
Reference ref = FirebaseStorage.instance.ref(path);
/// In some special cases detect firebase the contentType wrong,
/// so we make sure the contentType is set to jpg.
await ref.putData(bytes, SettableMetadata(contentType: 'image/jpg'));
} on FirebaseException catch (e) {
debugPrint(e.message);
}
if (mounted) Navigator.pop(context);
},
),
);
Supabase example
final _supabase = Supabase.instance.client;
ProImageEditor.asset(
'assets/demo.png',
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (bytes) async {
try {
String path = 'your-storage-path/my-image-name.jpg';
await _supabase.storage.from('my_bucket').uploadBinary(
path,
bytes,
retryAttempts: 3,
);
} catch (e) {
debugPrint(e.toString());
}
if (mounted) Navigator.pop(context);
},
),
);
Import-Export state history
The state history from the image editor can be exported and imported. However, it's important to note that the crop and rotate feature currently only allows exporting the final cropped image and not individual states. Additionally, all sticker widgets are converted into images and saved in that format during the export process.
Export example
await _editor.currentState?.exportStateHistory(
// All configurations are optional
configs: const ExportEditorConfigs(
exportPaint: true,
exportText: true,
exportCropRotate: false,
exportFilter: true,
exportEmoji: true,
exportSticker: true,
serializeSticker: true,
historySpan: ExportHistorySpan.all,
),
).toJson(); // or => toMap(), toFile()
Import example
_editor.currentState?.importStateHistory(
// or => fromMap(), fromJsonFile()
ImportStateHistory.fromJson(
/* Json-String from your exported state history */,
configs: const ImportEditorConfigs(
mergeMode: ImportEditorMergeMode.replace,
recalculateSizeAndPosition: true,
),
),
);
Initial import example
If you wish to open the editor directly with your exported state history, you can do so by utilizing the import feature. Simply load the exported state history into the editor, and it will recreate the previous editing session, allowing you to continue where you left off.
ProImageEditor.memory(
bytes,
key: _editor,
callbacks: ProImageEditorCallbacks(
onImageEditingComplete: (Uint8List bytes) async {
/*
Your code to handle the edited image. Upload it to your server as an example.
You can choose to use await, so that the loading-dialog remains visible until your code is ready, or no async, so that the loading-dialog closes immediately.
By default, the bytes are in `jpg` format.
*/
Navigator.pop(context);
},
),
configs: ProImageEditorConfigs(
stateHistoryConfigs: StateHistoryConfigs(
initStateHistory: ImportStateHistory.fromJson(
/* Json-String from your exported state history */,
configs: const ImportEditorConfigs(
mergeMode: ImportEditorMergeMode.replace,
recalculateSizeAndPosition: true,
),
),
),
),
);
Documentation
Interactive layers
Each layer, whether it's an emoji, text, or painting, is interactive, allowing you to manipulate them in various ways. You can move and scale layers using intuitive gestures. Holding a layer with one finger enables you to move it across the canvas. Holding a layer with one finger and using another to pinch or spread allows you to scale and rotate the layer.
On desktop devices, you can click and hold a layer with the mouse to move it. Additionally, using the mouse wheel lets you scale the layer. To rotate a layer, simply press the 'Shift' or 'Ctrl' key while interacting with it.
Editor Widget
Property | Description |
---|---|
byteArray |
Image data as a Uint8List from memory. |
file |
File object representing the image file. |
assetPath |
Path to the image asset. |
networkUrl |
URL of the image to be loaded from the network. |
configs |
Configuration options for the image editor. |
callbacks |
Callbacks for the image editor. |
Constructors
ProImageEditor.memory
Creates a ProImageEditor
widget for editing an image from memory.
ProImageEditor.file
Creates a ProImageEditor
widget for editing an image from a file.
ProImageEditor.asset
Creates a ProImageEditor
widget for editing an image from an asset.
ProImageEditor.network
Creates a ProImageEditor
widget for editing an image from a network URL.
ProImageEditorConfigs
Property Name | Description | Default Value |
---|---|---|
blurEditor |
Configuration options for the Blur Editor. | BlurEditorConfigs() |
cropRotateEditor |
Configuration options for the Crop and Rotate Editor. | CropRotateEditorConfigs() |
designMode |
The design mode for the Image Editor. | ImageEditorDesignModeE.material |
dialogConfigs |
Configuration for the loading dialog used in the editor. | DialogConfigs() |
emojiEditor |
Configuration options for the Emoji Editor. | EmojiEditorConfigs() |
filterEditor |
Configuration options for the Filter Editor. | FilterEditorConfigs() |
helperLines |
Configuration options for helper lines in the Image Editor. | HelperLineConfigs() |
heroTag |
A unique hero tag for the Image Editor widget. | 'Pro-Image-Editor-Hero' |
i18n |
Internationalization settings for the Image Editor. | I18n() |
imageGeneration |
Holds the configurations related to image generation. | ImageGenerationConfigs() |
layerInteraction |
Configuration options for the layer interaction behavior. | LayerInteractionConfigs() |
mainEditor |
Configuration options for the Main Editor. | MainEditorConfigs() |
paintEditor |
Configuration options for the Paint Editor. | PaintEditorConfigs() |
progressIndicatorConfigs |
Configuration for customizing progress indicators. | ProgressIndicatorConfigs() |
stateHistory |
Holds the configurations related to state history management. | StateHistoryConfigs() |
stickerEditor |
Configuration options for the Sticker Editor. | StickerEditorConfigs() |
textEditor |
Configuration options for the Text Editor. | TextEditorConfigs() |
theme |
The theme to be used for the Image Editor. | null |
tuneEditor |
Configuration options for the Tune Editor. | TuneEditorConfigs() |
ProImageEditorCallbacks
Property Name | Description | Default Value |
---|---|---|
onImageEditingStarted |
A callback function that is triggered when the image generation is started. | null |
onImageEditingComplete |
A callback function that will be called when the editing is done, returning the edited image as Uint8List with the format jpg . |
null |
onThumbnailGenerated |
A callback function that is called when the editing is complete and the thumbnail image is generated, along with capturing the original image as a raw ui.Image . If used, it will disable the onImageEditingComplete callback. |
null |
onCloseEditor |
A callback function that will be called before the image editor closes. | null |
mainEditorCallbacks |
Callbacks from the main editor. | null |
paintEditorCallbacks |
Callbacks from the paint editor. | null |
textEditorCallbacks |
Callbacks from the text editor. | null |
cropRotateEditorCallbacks |
Callbacks from the crop-rotate editor. | null |
filterEditorCallbacks |
Callbacks from the filter editor. | null |
blurEditorCallbacks |
Callbacks from the blur editor. | null |
i18n
Property Name | Description | Default Value |
---|---|---|
paintEditor |
Translations and messages specific to the painting editor. | I18nPaintingEditor() |
various |
Translations and messages for various parts of the editor. | I18nVarious() |
layerInteraction |
Translations and messages for layer interactions. | I18nLayerInteraction() |
textEditor |
Translations and messages specific to the text editor. | I18nTextEditor() |
filterEditor |
Translations and messages specific to the filter editor. | I18nFilterEditor() |
blurEditor |
Translations and messages specific to the blur editor. | I18nBlurEditor() |
emojiEditor |
Translations and messages specific to the emoji editor. | I18nEmojiEditor() |
stickerEditor |
Translations and messages specific to the sticker editor. | I18nStickerEditor() |
cropRotateEditor |
Translations and messages specific to the crop and rotate editor. | I18nCropRotateEditor() |
doneLoadingMsg |
Message displayed while changes are being applied. | Changes are being applied |
importStateHistoryMsg |
Message displayed during the import of state history. If the text is empty, no loading dialog will be shown. | Initialize Editor |
cancel |
Text for the "Cancel" action. | Cancel |
undo |
Text for the "Undo" action. | Undo |
redo |
Text for the "Redo" action. | Redo |
done |
Text for the "Done" action. | Done |
remove |
Text for the "Remove" action. | Remove |
i18n paintEditor
Property Name | Description | Default Value |
---|---|---|
bottomNavigationBarText |
Text for the bottom navigation bar item that opens the Painting Editor. | Paint |
freestyle |
Text for the "Freestyle" painting mode. | Freestyle |
arrow |
Text for the "Arrow" painting mode. | Arrow |
line |
Text for the "Line" painting mode. | Line |
rectangle |
Text for the "Rectangle" painting mode. | Rectangle |
circle |
Text for the "Circle" painting mode. | Circle |
dashLine |
Text for the "Dash line" painting mode. | Dash line |
lineWidth |
Text for the "Line width" tooltip. | Line width |
toggleFill |
Text for the "Toggle fill" tooltip. | Toggle fill |
undo |
Text for the "Undo" button. | Undo |
redo |
Text for the "Redo" button. | Redo |
done |
Text for the "Done" button. | Done |
back |
Text for the "Back" button. | Back |
smallScreenMoreTooltip |
The tooltip text displayed for the "More" option on small screens. | More |
i18n textEditor
Property | Description | Default Value |
---|---|---|
bottomNavigationBarText |
Text for the bottom navigation bar item | 'Text' |
inputHintText |
Placeholder text displayed in the text input field | 'Enter text' |
done |
Text for the "Done" button | 'Done' |
back |
Text for the "Back" button | 'Back' |
textAlign |
Text for the "Align text" setting | 'Align text' |
fontScale |
Text for the "Font Scale" setting | 'Font Scale' |
backgroundMode |
Text for the "Background mode" setting | 'Background mode' |
smallScreenMoreTooltip |
Tooltip text for the "More" option on small screens | 'More' |
i18n cropRotateEditor
Property Name | Description | Default Value |
---|---|---|
bottomNavigationBarText |
Text for the bottom navigation bar item that opens the Crop and Rotate Editor. | Crop/ Rotate |
rotate |
Text for the "Rotate" tooltip. | Rotate |
flip |
Text for the "Flip" tooltip. | Flip |
ratio |
Text for the "Ratio" tooltip. | Ratio |
back |
Text for the "Back" button. | Back |
cancel |
Text for the "Cancel" button. | Cancel |
done |
Text for the "Done" button. | Done |
reset |
Text for the "Reset" button. | Reset |
undo |
Text for the "Undo" button. | Undo |
redo |
Text for the "Redo" button. | Redo |
smallScreenMoreTooltip |
The tooltip text displayed for the "More" option on small screens. | More |
i18n filterEditor
Property | Description | Default Value |
---|---|---|
applyFilterDialogMsg |
Text displayed when a filter is being applied | 'Filter is being applied.' |
bottomNavigationBarText |
Text for the bottom navigation bar item | 'Filter' |
back |
Text for the "Back" button in the Filter Editor | 'Back' |
done |
Text for the "Done" button in the Filter Editor | 'Done' |
filters |
Internationalization settings for individual filters | I18nFilters() |
i18n blurEditor
Property | Description | Default Value |
---|---|---|
applyBlurDialogMsg |
Text displayed when a filter is being applied | 'Blur is being applied.' |
bottomNavigationBarText |
Text for the bottom navigation bar item | 'Blur' |
back |
Text for the "Back" button in the Blur Editor | 'Back' |
done |
Text for the "Done" button in the Blur Editor | 'Done' |
i18n emojiEditor
Property Name | Description | Default Value |
---|---|---|
bottomNavigationBarText |
Text for the bottom navigation bar item that opens the Emoji Editor. | Emoji |
noRecents |
Text which shows there are no recent selected emojis. | No Recents |
search |
Hint text in the search field. | Search |
i18n stickerEditor
Property | Description | Default Value |
---|---|---|
bottomNavigationBarText |
Text for the bottom navigation bar item that opens the Sticker Editor. | 'Stickers' |
i18n various
Property | Description | Default Value |
---|---|---|
loadingDialogMsg |
Text for the loading dialog message. | 'Please wait...' |
closeEditorWarningTitle |
Title for the warning message when closing the Image Editor. | 'Close Image Editor?' |
closeEditorWarningMessage |
Warning message when closing the Image Editor. | 'Are you sure you want to close the Image Editor? Your changes will not be saved.' |
closeEditorWarningConfirmBtn |
Text for the confirmation button in the close editor warning dialog. | 'OK' |
closeEditorWarningCancelBtn |
Text for the cancel button in the close editor warning dialog. | 'Cancel' |
Contributing
I welcome contributions from the open-source community to make this project even better. Whether you want to report a bug, suggest a new feature, or contribute code, I appreciate your help.
Bug Reports and Feature Requests
If you encounter a bug or have an idea for a new feature, please open an issue on my GitHub Issues page. I will review it and discuss the best approach to address it.
Code Contributions
If you'd like to contribute code to this project, please follow these steps:
- Fork the repository to your GitHub account.
- Clone your forked repository to your local machine.
git clone https://github.com/hm21/pro_image_editor.git
Contributors
Made with contrib.rocks.
Included Packages
This package uses several Flutter packages to provide a seamless editing experience. A big thanks to the authors of these amazing packages. Hereβs a list of the packages we used in this project:
From these packages, only a small part of the code is used, with some code changes that better fit to the image editor.
Libraries
- common/editor_style_constants
- common/editor_various_constants
- common/editor_web_constants
- designs/frosted_glass/frosted_glass
- designs/frosted_glass/frosted_glass_appbar
- designs/frosted_glass/frosted_glass_blur_appbar
- designs/frosted_glass/frosted_glass_close_dialog
- designs/frosted_glass/frosted_glass_crop_rotate_toolbar
- designs/frosted_glass/frosted_glass_effect
- designs/frosted_glass/frosted_glass_filter_appbar
- designs/frosted_glass/frosted_glass_loading_dialog
- designs/frosted_glass/frosted_glass_paint_appbar
- designs/frosted_glass/frosted_glass_paint_bottombar
- designs/frosted_glass/frosted_glass_sticker_editor
- designs/frosted_glass/frosted_glass_text_appbar
- designs/frosted_glass/frosted_glass_text_bottombar
- designs/frosted_glass/frosted_glass_text_size_slider
- designs/frosted_glass/frosted_glass_tune_appbar
- designs/frosted_glass/frosted_glass_tune_bottombar
- designs/grounded/grounded_blur_bar
- designs/grounded/grounded_bottom_bar
- designs/grounded/grounded_bottom_wrapper
- designs/grounded/grounded_crop_rotate_bar
- designs/grounded/grounded_design
- designs/grounded/grounded_emoji_editor
- designs/grounded/grounded_filter_bar
- designs/grounded/grounded_main_bar
- designs/grounded/grounded_paint_bar
- designs/grounded/grounded_sticker_editor
- designs/grounded/grounded_text_bar
- designs/grounded/grounded_text_size_slider
- designs/grounded/grounded_tune_bar
- designs/grounded/utils/grounded_configs
- designs/whatsapp/whatsapp
- designs/whatsapp/whatsapp_appbar
- designs/whatsapp/whatsapp_color_picker
- designs/whatsapp/whatsapp_crop_rotate_toolbar
- designs/whatsapp/whatsapp_done_btn
- designs/whatsapp/whatsapp_filters
- designs/whatsapp/whatsapp_paint_appbar
- designs/whatsapp/whatsapp_paint_bottombar
- designs/whatsapp/whatsapp_paint_colorpicker
- designs/whatsapp/whatsapp_sticker_editor
- designs/whatsapp/whatsapp_text_appbar
- designs/whatsapp/whatsapp_text_bottombar
- designs/whatsapp/whatsapp_text_colorpicker
- designs/whatsapp/whatsapp_text_size_slider
- extensions/color_extension
- mixins/converted_callbacks
- mixins/converted_configs
- mixins/editor_callbacks_mixin
- mixins/editor_configs_mixin
- mixins/extended_loop
- mixins/main_editor/main_editor_global_keys
- mixins/standalone_editor
- models/crop_rotate_editor/aspect_ratio_item
- models/crop_rotate_editor/rotate_direction
- models/crop_rotate_editor/transform_factors
- models/custom_widgets/blur_editor_widgets
- models/custom_widgets/crop_rotate_editor_widgets
- models/custom_widgets/dialog_widgets
- models/custom_widgets/filter_editor_widgets
- models/custom_widgets/layer_interaction_widgets
- models/custom_widgets/main_editor_widgets
- models/custom_widgets/paint_editor_widgets
- models/custom_widgets/progress_indicator_widgets
- models/custom_widgets/text_editor_widgets
- models/custom_widgets/tune_editor_widgets
- models/custom_widgets/utils/custom_widgets_standalone_editor
- models/custom_widgets/utils/custom_widgets_typedef
- models/editor_callbacks/blur_editor_callbacks
- models/editor_callbacks/crop_rotate_editor_callbacks
- models/editor_callbacks/editor_callbacks_typedef
- models/editor_callbacks/emoji_editor_callbacks
- models/editor_callbacks/filter_editor_callbacks
- models/editor_callbacks/main_editor_callbacks
- models/editor_callbacks/paint_editor_callbacks
- models/editor_callbacks/pro_image_editor_callbacks
- models/editor_callbacks/standalone_editor_callbacks
- models/editor_callbacks/sticker_editor_callbacks
- models/editor_callbacks/text_editor_callbacks
- models/editor_callbacks/tune_editor_callbacks
- models/editor_callbacks/utils/sub_editors_name
- models/editor_configs/blur_editor_configs
- models/editor_configs/crop_rotate_editor_configs
- models/editor_configs/dialog_configs
- models/editor_configs/emoji_editor_configs
- models/editor_configs/filter_editor_configs
- models/editor_configs/helper_lines_configs
- models/editor_configs/image_generation_configs/image_generation_configs
- models/editor_configs/image_generation_configs/output_formats
- models/editor_configs/image_generation_configs/processor_configs
- models/editor_configs/layer_interaction_configs
- models/editor_configs/main_editor_configs
- models/editor_configs/paint_editor_configs
- models/editor_configs/pro_image_editor_configs
- models/editor_configs/progress_indicator_configs
- models/editor_configs/state_history_configs
- models/editor_configs/sticker_editor_configs
- models/editor_configs/text_editor_configs
- models/editor_configs/tune_editor_configs
- models/editor_configs/utils/editor_safe_area
- models/editor_image
- models/history/last_layer_interaction_position
- models/history/state_history
- models/i18n/i18n
- models/i18n/i18n_blur_editor
- models/i18n/i18n_crop_rotate_editor
- models/i18n/i18n_emoji_editor
- models/i18n/i18n_filter_editor
- models/i18n/i18n_layer_interaction
- models/i18n/i18n_paint_editor
- models/i18n/i18n_sticker_editor
- models/i18n/i18n_text_editor
- models/i18n/i18n_tune_editor
- models/i18n/i18n_various
- models/icons/blur_editor_icons
- models/icons/crop_rotate_editor_icons
- models/icons/emoji_editor_icons
- models/icons/filter_editor_icons
- models/icons/layer_interaction_icons
- models/icons/main_editor_icons
- models/icons/paint_editor_icons
- models/icons/sticker_editor_icons
- models/icons/text_editor_icons
- models/icons/tune_editor_icons
- models/import_export/export_state_history
- models/import_export/export_state_history_configs
- models/import_export/import_state_history
- models/import_export/import_state_history_configs
- models/import_export/utils/export_import_enum
- models/import_export/utils/export_import_version
- models/init_configs/blur_editor_init_configs
- models/init_configs/crop_rotate_editor_init_configs
- models/init_configs/editor_init_configs
- models/init_configs/filter_editor_init_configs
- models/init_configs/paint_editor_init_configs
- models/init_configs/tune_editor_init_configs
- models/layer/layer
- models/layer/layer_background_mode
- models/multi_threading/thread_capture_model
- models/multi_threading/thread_request_model
- models/multi_threading/thread_response_model
- models/multi_threading/thread_task_model
- models/paint_editor/paint_bottom_bar_item
- models/paint_editor/painted_model
- models/styles/adaptive_dialog_style
- models/styles/blur_editor_style
- models/styles/crop_rotate_editor_style
- models/styles/dialog_style
- models/styles/draggable_sheet_style
- models/styles/emoji_editor_style
- models/styles/filter_editor_style
- models/styles/helper_line_style
- models/styles/layer_interaction_style
- models/styles/loading_dialog_style
- models/styles/main_editor_style
- models/styles/paint_editor_style
- models/styles/sticker_editor_style
- models/styles/sub_editor_page_style
- models/styles/text_editor_style
- models/styles/tune_editor_style
- models/styles/types/style_types
- models/transform_helper
- models/tune_editor/tune_adjustment_item
- models/tune_editor/tune_adjustment_matrix
- modules/blur_editor/blur_editor
- modules/crop_rotate_editor/crop_rotate_editor
- modules/crop_rotate_editor/utils/crop_area_history
- modules/crop_rotate_editor/utils/crop_area_part
- modules/crop_rotate_editor/utils/crop_aspect_ratios
- modules/crop_rotate_editor/utils/crop_desktop_interaction_manager
- modules/crop_rotate_editor/utils/crop_layer_painter
- modules/crop_rotate_editor/utils/rotate_angle
- modules/crop_rotate_editor/widgets/crop_aspect_ratio_options
- modules/crop_rotate_editor/widgets/crop_corner_painter
- modules/emoji_editor/emoji_editor
- modules/emoji_editor/utils/emoji_state_manager
- modules/emoji_editor/widgets/emoji_cell_extended
- modules/emoji_editor/widgets/emoji_editor_bottom_bar
- modules/emoji_editor/widgets/emoji_editor_category_view
- modules/emoji_editor/widgets/emoji_editor_full_screen_search
- modules/emoji_editor/widgets/emoji_editor_header_search
- modules/emoji_editor/widgets/emoji_picker_view
- modules/filter_editor/filter_editor
- modules/filter_editor/types/filter_matrix
- modules/filter_editor/utils/filter_generator/filter_addons
- modules/filter_editor/utils/filter_generator/filter_model
- modules/filter_editor/utils/filter_generator/filter_presets
- modules/filter_editor/widgets/filter_editor_item_list
- modules/filter_editor/widgets/filter_generator
- modules/filter_editor/widgets/filtered_image
- modules/main_editor/main_editor
- modules/main_editor/utils/desktop_interaction_manager
- modules/main_editor/utils/layer_copy_manager
- modules/main_editor/utils/layer_interaction_manager
- modules/main_editor/utils/main_editor_controllers
- modules/main_editor/utils/sizes_manager
- modules/main_editor/utils/state_manager
- modules/main_editor/utils/whatsapp_helper
- modules/paint_editor/paint_editor
- modules/paint_editor/utils/paint_controller
- modules/paint_editor/utils/paint_desktop_interaction_manager
- modules/paint_editor/utils/paint_editor_enum
- modules/paint_editor/utils/paint_element
- modules/paint_editor/widgets/draw_paint_item
- modules/paint_editor/widgets/paint_canvas
- modules/sticker_editor/sticker_editor
- modules/text_editor/text_editor
- modules/text_editor/widgets/text_editor_bottom_bar
- modules/tune_editor/tune_editor
- modules/tune_editor/utils/tune_presets
- plugins/defer_pointer/defer_pointer
- plugins/rounded_background_text/rounded_background_text
- plugins/rounded_background_text/src/rounded_background_text
- plugins/rounded_background_text/src/rounded_background_text_field
- plugins/rounded_background_text/src/rounded_background_text_span
- pro_image_editor
- utils/content_recorder.dart/content_recorder
- utils/content_recorder.dart/content_recorder_controller
- utils/content_recorder.dart/threads_managers/isolate/isolate_manager
- utils/content_recorder.dart/threads_managers/isolate/isolate_thread
- utils/content_recorder.dart/threads_managers/isolate/isolate_thread_code
- utils/content_recorder.dart/threads_managers/threads/thread
- utils/content_recorder.dart/threads_managers/threads/thread_manager
- utils/content_recorder.dart/threads_managers/web_worker/web_utils
- utils/content_recorder.dart/threads_managers/web_worker/web_worker_manager
- utils/content_recorder.dart/threads_managers/web_worker/web_worker_manager_dummy
- utils/content_recorder.dart/threads_managers/web_worker/web_worker_thread
- utils/content_recorder.dart/utils/convert_raw_image
- utils/content_recorder.dart/utils/dart_ui_remove_transparent_image_areas
- utils/content_recorder.dart/utils/encode_image
- utils/content_recorder.dart/utils/encoder/jpeg_encoder
- utils/content_recorder.dart/utils/generate_high_quality_image
- utils/content_recorder.dart/utils/processor_helper
- utils/content_recorder.dart/utils/record_invisible_widget
- utils/converters
- utils/custom_page_transition
- utils/debounce
- utils/decode_image
- utils/design_mode
- utils/layer_transform_generator
- utils/parser/double_parser
- utils/parser/int_parser
- utils/parser/size_parser
- utils/pro_image_editor_icons
- utils/pro_image_editor_mode
- utils/swipe_mode
- utils/theme_functions
- utils/transition_timing
- utils/transparent_image_bytes
- utils/unique_id_generator
- web/web_worker
- widgets/adaptive_dialog
- widgets/animated/fade_in_base
- widgets/animated/fade_in_left
- widgets/animated/fade_in_up
- widgets/auto_image
- widgets/bottom_sheets_header_row
- widgets/color_picker/bar_color_picker
- widgets/color_picker/color_picker_configs
- widgets/custom_widgets/reactive_custom_appbar
- widgets/custom_widgets/reactive_custom_widget
- widgets/extended/extended_custom_paint
- widgets/extended/extended_interactive_viewer
- widgets/extended/extended_mouse_cursor
- widgets/extended/extended_pop_scope
- widgets/extended/extended_transform_scale
- widgets/extended/extended_transform_translate
- widgets/layer_interaction_helper/layer_interaction_border_painter
- widgets/layer_interaction_helper/layer_interaction_helper_widget
- widgets/layer_stack
- widgets/layer_widget
- widgets/outside_gestures/crop_rotate_gesture_detector
- widgets/outside_gestures/outside_gesture_behavior
- widgets/outside_gestures/outside_gesture_detector
- widgets/outside_gestures/outside_gesture_listener
- widgets/outside_gestures/outside_raw_gesture_detector
- widgets/outside_gestures/outside_render_proxy_box
- widgets/outside_gestures/outside_render_semantics_gesture_handler
- widgets/overlays/loading_dialog/animations/loading_dialog_base_animation
- widgets/overlays/loading_dialog/animations/loading_dialog_opacity_animation
- widgets/overlays/loading_dialog/loading_dialog
- widgets/overlays/loading_dialog/models/loading_dialog_overlay_details
- widgets/platform_circular_progress_indicator
- widgets/screen_resize_detector
- widgets/transform/transformed_content_generator