Flutter图片编辑插件pro_editor_image_toni的使用
Flutter 图片编辑插件 pro_editor_image_toni 的使用
简介
pro_editor_image_toni
是一个基于 pro_editor_img
修改的 Flutter 插件。此插件添加了谷歌字体到编辑器中。
完整示例代码
// 忽略对于引用包的检查
import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:example/editor_teste.dart';
import 'package:sizer/sizer.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart' hide Category;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:pro_editor_image_toni/pro_image_editor.dart';
import 'package:pro_editor_image_toni/widgets/loading_dialog.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// 此小部件是您的应用程序的根。
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Pro-Image-Editor',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue.shade800),
useMaterial3: true,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _editor = GlobalKey<ProImageEditorState>();
var statHistory;
List<File> fileImgList = [];
Future<Uint8List> loadImageBytes() async {
final ByteData data = await rootBundle.load('assets/demo.png');
return data.buffer.asUint8List();
}
setHistoric() {
return _editor.currentState?.importStateHistory(
// 或者 => fromMap(), fromJsonFile()
ImportStateHistory.fromMap(
statHistory,
configs: const ImportEditorConfigs(
mergeMode: ImportEditorMergeMode.replace,
recalculateSizeAndPosition: true,
),
),
);
}
Future selectFile(BuildContext context) async {
final result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result == null) return;
setState(() {
fileImgList.add(File(result.files.first.path!));
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Pro-Image-Editor'),
),
body: Center(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
OutlinedButton.icon(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProImageEditor.asset(
'assets/demo.png',
configs: statHistory != null
? ProImageEditorConfigs(
initStateHistory:
ImportStateHistory.fromMap(statHistory))
: const ProImageEditorConfigs(),
key: _editor,
onImageEditingComplete: (bytes) async {
var statHist = await _editor.currentState!
.exportStateHistory(
configs: const ExportEditorConfigs(
exportCropRotate: true,
exportEmoji: true,
exportFilter: true,
exportPainting: true,
exportSticker: true,
exportText: true,
historySpan: ExportHistorySpan.all,
))
.toMap();
setState(() {
statHistory = statHist;
});
Navigator.pop(context);
},
),
),
);
},
icon: const Icon(Icons.folder_outlined),
label: const Text('从资源加载编辑器'),
),
const SizedBox(height: 30),
if (!kIsWeb) ...[
OutlinedButton.icon(
onPressed: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result != null && context.mounted) {
File file = File(result.files.single.path!);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProImageEditor.file(
file,
onImageEditingComplete: (Uint8List bytes) async {
Navigator.pop(context);
},
),
),
);
}
},
icon: const Icon(Icons.sd_card_outlined),
label: const Text('从文件加载编辑器'),
),
const SizedBox(height: 30),
],
OutlinedButton.icon(
onPressed: () async {
LoadingDialog loading = LoadingDialog()
..show(
context,
theme: Theme.of(context),
imageEditorTheme: const ImageEditorTheme(
loadingDialogTextColor: Colors.black,
),
designMode: ImageEditorDesignModeE.material,
i18n: const I18n(),
);
var url = 'https://picsum.photos/2000';
var bytes = await fetchImageAsUint8List(url);
if (context.mounted) await loading.hide(context);
if (context.mounted) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProImageEditor.memory(
bytes,
key: _editor,
onImageEditingComplete: (bytes) async {
Navigator.pop(context);
},
configs: ProImageEditorConfigs(
i18n: const I18n(
various: I18nVarious(
loadingDialogMsg: '请等待...',
closeEditorWarningTitle: '关闭图像编辑器?',
closeEditorWarningMessage:
'确定要关闭图像编辑器吗?你的更改将不会被保存。',
closeEditorWarningConfirmBtn: '确定',
closeEditorWarningCancelBtn: '取消',
),
paintEditor: I18nPaintingEditor(
bottomNavigationBarText: '绘图',
freestyle: '自由画',
arrow: '箭头',
line: '直线',
rectangle: '矩形',
circle: '圆形',
dashLine: '虚线',
lineWidth: '线宽',
toggleFill: '切换填充',
undo: '撤销',
redo: '重做',
done: '完成',
back: '返回',
smallScreenMoreTooltip: '更多',
),
textEditor: I18nTextEditor(
inputHintText: '输入文本',
bottomNavigationBarText: '文本',
back: '返回',
done: '完成',
textAlign: '对齐文本',
backgroundMode: '背景模式',
smallScreenMoreTooltip: '更多',
),
cropRotateEditor: I18nCropRotateEditor(
bottomNavigationBarText: '裁剪/旋转',
rotate: '旋转',
ratio: '比例',
back: '返回',
done: '完成',
aspectRatioFree: '自由',
aspectRatioOriginal: '原始',
prepareImageDialogMsg: '请等待',
applyChangesDialogMsg: '请等待',
smallScreenMoreTooltip: '更多',
),
filterEditor: I18nFilterEditor(
applyFilterDialogMsg:
'正在应用滤镜...',
bottomNavigationBarText: '滤镜',
back: '返回',
done: '完成',
filters: I18nFilters(
none: '无滤镜',
addictiveBlue: '上瘾蓝',
addictiveRed: '上瘾红',
aden: '腺',
amaro: '阿玛罗',
ashby: '阿什比',
brannan: '布兰南',
brooklyn: '布鲁克林',
charmes: '查尔姆斯',
clarendon: '克拉伦登',
crema: '克雷玛',
dogpatch: '狗补丁',
earlybird: '早鸟',
f1977: '1977',
gingham: '金吉姆',
ginza: '银座',
hefe: '赫菲',
helena: '海伦娜',
hudson: '哈德森',
inkwell: '墨水井',
juno: '朱诺',
kelvin: '开尔文',
lark: '云雀',
loFi: '低音',
ludwig: '路德维希',
maven: '马文',
mayfair: '梅菲尔',
moon: '月亮',
nashville: '纳什维尔',
perpetua: '佩雷图亚',
reyes: '雷耶斯',
rise: '升起',
sierra: '西耶拉',
skyline: '天际线',
slumber: '睡眠',
stinson: '斯廷森',
sutro: '苏特罗',
toaster: '烤面包机',
valencia: '瓦伦西亚',
vesper: '韦斯珀',
walden: '沃登',
willow: '柳树',
xProII: 'XPro II',
),
),
emojiEditor: I18nEmojiEditor(
bottomNavigationBarText: '表情',
),
stickerEditor: I18nStickerEditor(
bottomNavigationBarText: '贴纸编辑器',
),
cancel: '取消',
undo: '撤销',
redo: '重做',
done: '完成',
remove: '移除',
doneLoadingMsg: '正在应用更改',
),
helperLines: const HelperLines(
showVerticalLine: true,
showHorizontalLine: true,
showRotateLine: true,
hitVibration: true,
),
customWidgets: const ImageEditorCustomWidgets(),
imageEditorTheme: const ImageEditorTheme(
layerHoverCursor: SystemMouseCursors.move,
helperLine: HelperLineTheme(
horizontalColor: Color(0xFF1565C0),
verticalColor: Color(0xFF1565C0),
rotateColor: Color(0xFFE91E63),
),
paintingEditor: PaintingEditorTheme(
appBarBackgroundColor: Color(0xFF000000),
lineWidthBottomSheetColor: Color(0xFF252728),
appBarForegroundColor: Color(0xFFE1E1E1),
background: Color.fromARGB(255, 22, 22, 22),
bottomBarColor: Color(0xFF000000),
bottomBarActiveItemColor: Color(0xFF004C9E),
bottomBarInactiveItemColor: Color(0xFFEEEEEE),
),
textEditor: TextEditorTheme(
appBarBackgroundColor: Color(0xFF000000),
appBarForegroundColor: Color(0xFFE1E1E1),
background: Color.fromARGB(155, 0, 0, 0),
inputHintColor: Color(0xFFBDBDBD),
inputCursorColor: Color(0xFF004C9E),
),
cropRotateEditor: CropRotateEditorTheme(
appBarBackgroundColor: Color(0xFF000000),
appBarForegroundColor: Color(0xFFE1E1E1),
background: Color.fromARGB(255, 22, 22, 22),
cropCornerColor: Color(0xFF004C9E),
),
filterEditor: FilterEditorTheme(
appBarBackgroundColor: Color(0xFF000000),
appBarForegroundColor: Color(0xFFE1E1E1),
previewTextColor: Color(0xFFE1E1E1),
background: Color.fromARGB(255, 22, 22, 22),
),
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(
paintingEditor: IconsPaintingEditor(
bottomNavBar: Icons.edit_rounded,
lineWeight: Icons.line_weight_rounded,
freeStyle: Icons.edit,
arrow: Icons.arrow_right_alt_outlined,
line: Icons.horizontal_rule,
fill: Icons.format_color_fill,
noFill: Icons.format_color_reset,
rectangle: Icons.crop_free,
circle: Icons.lens_outlined,
dashLine: Icons.power_input,
),
textEditor: IconsTextEditor(
bottomNavBar: Icons.text_fields,
alignLeft: Icons.align_horizontal_left_rounded,
alignCenter:
Icons.align_horizontal_center_rounded,
alignRight:
Icons.align_horizontal_right_rounded,
backgroundMode: Icons.layers_rounded,
),
cropRotateEditor: IconsCropRotateEditor(
bottomNavBar: Icons.crop_rotate_rounded,
rotate: Icons.rotate_90_degrees_ccw_outlined,
aspectRatio: Icons.crop,
),
filterEditor: IconsFilterEditor(
bottomNavBar: Icons.filter,
),
emojiEditor: IconsEmojiEditor(
bottomNavBar:
Icons.sentiment_satisfied_alt_rounded,
),
stickerEditor: IconsStickerEditor(
bottomNavBar: Icons.layers_outlined,
),
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(
enabled: true,
hasOptionFreeStyle: true,
hasOptionArrow: true,
hasOptionLine: true,
hasOptionRect: true,
hasOptionCircle: true,
hasOptionDashLine: true,
canToggleFill: true,
canChangeLineWidth: true,
initialFill: false,
showColorPicker: true,
freeStyleHighPerformanceScaling: true,
initialStrokeWidth: 10.0,
initialColor: Color(0xffff0000),
initialPaintMode: PaintModeE.freeStyle,
),
textEditorConfigs: const TextEditorConfigs(
enabled: true,
canToggleTextAlign: true,
canToggleBackgroundMode: true,
initFontSize: 24.0,
initialTextAlign: TextAlign.center,
initialBackgroundColorMode:
LayerBackgroundColorModeE.backgroundAndColor,
),
cropRotateEditorConfigs:
const CropRotateEditorConfigs(
enabled: true,
canRotate: true,
canChangeAspectRatio: true,
initAspectRatio: CropAspectRatios.custom,
),
filterEditorConfigs: FilterEditorConfigs(
enabled: true,
filterList: presetFiltersList,
),
emojiEditorConfigs: const EmojiEditorConfigs(
enabled: true,
initScale: 5.0,
textStyle: TextStyle(
fontFamilyFallback: ['Apple Color Emoji']),
checkPlatformCompatibility: true,
/* emojiSet: [
CategoryEmoji(
Category.ANIMALS,
[
Emoji(
'emoji',
'name',
hasSkinTone: false,
),
],
)
], */
),
designMode: ImageEditorDesignModeE.material,
heroTag: 'hero',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue.shade800,
brightness: Brightness.dark,
),
),
),
),
),
);
}
},
icon: const Icon(Icons.memory_outlined),
label: const Text('从内存加载编辑器'),
),
const SizedBox(height: 30),
OutlinedButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProImageEditor.network(
'https://picsum.photos/id/237/2000',
onImageEditingComplete: (byte) async {
Navigator.pop(context);
configs:
ProImageEditorConfigs(
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: 4,
shrinkWrap: true,
itemBuilder: (context, index) {
Widget widget = Image.network(
'https://picsum.photos/id/${(index + 3) * 3}/2000',
width: 120,
height: 120,
fit: BoxFit.cover,
loadingBuilder: (context, child,
loadingProgress) {
return AnimatedSwitcher(
layoutBuilder: (currentChild,
previousChildren) {
return SizedBox(
width: 120,
height: 120,
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: <Widget>[
...previousChildren,
if (currentChild != null)
currentChild,
],
),
);
},
duration: const Duration(
milliseconds: 200),
child: loadingProgress == null
? child
: Center(
child:
CircularProgressIndicator(
value: loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress
.expectedTotalBytes!
: null,
),
),
);
},
);
return GestureDetector(
onTap: () => setLayer(widget),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: widget,
),
);
},
),
),
);
},
),
);
},
),
),
);
},
icon: const Icon(Icons.public_outlined),
label: const Text('从网络加载编辑器'),
),
const SizedBox(height: 30),
OutlinedButton.icon(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProImageEditor.network(
'https://picsum.photos/id/176/2000',
onImageEditingComplete: (bytes) async {
Navigator.pop(context);
},
configs: ProImageEditorConfigs(
stickerEditorConfigs: StickerEditorConfigs(
enabled: true,
buildStickers: (setLayer) {
return ClipRRect(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20)),
child: Container(
width: 800,
color: const Color.fromARGB(
255, 224, 239, 251),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(
vertical: 10),
child: fileImgList.isNotEmpty
? widget
: const Text(
"未选择任何图片")),
ElevatedButton(
onPressed: () =>
selectFile(context),
child:
const Text("选择图片")),
GestureDetector(
onTap: () => setLayer(widget!),
child: widget!),
],
)),
);
},
),
),
),
),
);
},
icon: const Icon(Icons.layers_outlined),
label: const Text('带贴纸的编辑器'),
),
const SizedBox(height: 30),
OutlinedButton.icon(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProImageEditor.asset(
'assets/demo.png',
onImageEditingComplete: (bytes) async {
Navigator.pop(context);
},
configs: ProImageEditorConfigs(
emojiEditorConfigs: EmojiEditorConfigs(
checkPlatformCompatibility: false,
textStyle: DefaultEmojiTextStyle.copyWith(
fontFamily:
GoogleFonts.notoColorEmoji().fontFamily,
),
),
),
),
),
);
},
icon: const Icon(Icons.emoji_emotions_outlined),
label: const Text('谷歌字体表情'),
),
],
),
),
),
);
}
}
更多关于Flutter图片编辑插件pro_editor_image_toni的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter图片编辑插件pro_editor_image_toni的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,关于Flutter图片编辑插件 pro_editor_image_toni
的使用,以下是一个基本的代码示例,展示了如何集成和使用该插件进行图片编辑。请注意,由于插件的具体API可能会随着版本更新而变化,因此以下代码仅供参考,并可能需要根据实际插件版本进行调整。
首先,确保你已经在 pubspec.yaml
文件中添加了 pro_editor_image_toni
依赖:
dependencies:
flutter:
sdk: flutter
pro_editor_image_toni: ^最新版本号 # 替换为实际最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来,在你的 Flutter 项目中,你可以按照以下步骤使用 pro_editor_image_toni
插件:
- 导入插件:
import 'package:pro_editor_image_toni/pro_editor_image_toni.dart';
- 定义编辑页面:
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:pro_editor_image_toni/pro_editor_image_toni.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ImageEditorPage(),
);
}
}
class ImageEditorPage extends StatefulWidget {
@override
_ImageEditorPageState createState() => _ImageEditorPageState();
}
class _ImageEditorPageState extends State<ImageEditorPage> {
File? _imageFile;
final ImagePicker _picker = ImagePicker();
Future<void> _pickImage() async {
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
_imageFile = File(pickedFile!.path);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Editor'),
),
body: Center(
child: _imageFile == null
? ElevatedButton(
onPressed: _pickImage,
child: Text('Pick Image'),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.file(_imageFile!),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageEditorScreen(
image: _imageFile!,
),
),
);
if (result != null) {
setState(() {
_imageFile = result;
});
}
},
child: Text('Edit Image'),
),
],
),
),
);
}
}
class ImageEditorScreen extends StatefulWidget {
final File image;
ImageEditorScreen({required this.image});
@override
_ImageEditorScreenState createState() => _ImageEditorScreenState();
}
class _ImageEditorScreenState extends State<ImageEditorScreen> {
File? editedImage;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Image'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.file(widget.image),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
// 打开图片编辑器
final result = await ProImageEditor.editImage(
context,
widget.image.path,
);
if (result != null) {
setState(() {
editedImage = File(result);
});
}
},
child: Text('Open Editor'),
),
if (editedImage != null) ...[
SizedBox(height: 20),
Image.file(editedImage!),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 返回编辑后的图片
Navigator.pop(context, editedImage);
},
child: Text('Save and Return'),
),
],
],
),
),
);
}
}
在这个示例中,我们首先定义了一个简单的图片选择器,允许用户从相册中选择一张图片。然后,我们定义了一个 ImageEditorScreen
页面,其中包含了 ProImageEditor.editImage
方法的调用,该方法用于打开图片编辑器。
请注意,ProImageEditor.editImage
方法的具体参数和返回值可能会根据插件版本有所不同。因此,请参考插件的官方文档或源代码以获取最新和最准确的信息。
此外,这个示例假设 pro_editor_image_toni
插件提供了一个名为 ProImageEditor
的类和一个名为 editImage
的静态方法。如果实际情况有所不同,请根据实际情况进行调整。
最后,由于插件的集成和使用可能涉及复杂的权限处理(如读写存储权限),请确保你的应用已经正确配置了必要的权限,并在运行时请求这些权限。