Flutter富文本编辑器插件flu_editor的使用
Flutter富文本编辑器插件flu_editor的使用
入门
flu_editor
是一个用于照片和视频的颜色滤镜编辑插件。以下是使用 flu_editor
的步骤和示例代码。
功能概述
flu_editor
提供了多种图像编辑功能,以下是工具类的功能概述:
EditorUtil 工具类
页面导航说明
-
goFluEditor
:跳转到编辑器页面。 -
参数说明:
context
:当前上下文,通常使用BuildContext
来启动编辑器。orignal
:原始图片路径,用于传入需要编辑的图片。type
:编辑类型 (null 代表进首页)。singleEditorSave
:单独进到某个功能页面,关闭是否保存图片到相册。vipStatusCb
:一个回调函数,返回用户是否为 VIP 用户。vipActionCb
:一个回调函数,当用户非 VIP 时,触发跳转到订阅页面。saveCb
:一个回调函数,用于保存编辑后的图片,参数为保存路径。loadWidgetCb
:加载提示回调,用于显示加载动画,传入islight
(是否为浅色模式)、size
(进度条大小)、stroke
(进度条宽度)。toastActionCb
:一个回调函数,显示自定义提示信息(如 “保存成功”)。effectsCb
:回调函数,用于获取并处理滤镜配方。saveEffectCb
:回调函数,保存自定义滤镜配方。deleteEffectCb
:回调函数,删除已保存的滤镜配方。filtersCb
:回调函数,用于获取滤镜列表。stickersCb
:回调函数,用于获取贴纸列表。fontsCb
:回调函数,用于获取字体列表。framesCb
:回调函数,用于获取边框列表。homeSavedCb
:回调函数,编辑器主页保存图片。
-
内部页面路由:
- 编辑器首页进入的具体功能区(宿主app不要直接调用,要通过
goFluEditor(type)
进入):goCropPage
:跳转到裁剪页面。goColorsPage
:跳转到颜色调整页面。goFilterPage
:跳转到滤镜编辑页面。goStickerPage
:跳转到贴纸编辑页面。goFontPage
:跳转到字体编辑页面。goFramePage
:跳转到相框编辑页面。
- 编辑器首页进入的具体功能区(宿主app不要直接调用,要通过
多语言配置
在 MaterialApp
内添加如下 delegate 以及 supportedLocales
:
MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
EditorLang.delegate
],
supportedLocales: [...EditorLang.delegate.supportedLocales],
)
使用示例
以下是如何使用 flu_editor
进行图像编辑的示例:
import 'dart:convert';
import 'dart:io';
import 'package:flu_editor_example/route_page.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flu_editor/generated/l10n.dart';
import 'package:flu_editor/flu_editor.dart';
import 'package:gallery_saver_plus/gallery_saver.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _fluEditorPlugin = FluEditor();
/// 当前输入图
String _currentImage = '';
bool isVipUser = false;
[@override](/user/override)
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion = await _fluEditorPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
EditorLang.delegate
],
supportedLocales: [...EditorLang.delegate.supportedLocales],
home: Builder(builder: (context) {
return Scaffold(
appBar: AppBar(
title: const Center(
child: Text('FluEditorApp'),
),
),
body: Column(
children: [
Expanded(
child: Container(
width: double.infinity,
color: Colors.grey,
child: Stack(alignment: Alignment.center, children: [
_currentImage.isEmpty
? const SizedBox()
: Image.file(File(_currentImage)),
GestureDetector(
onTap: () {
_pickImage(context);
},
child: Container(
height: 200,
width: 200,
color: Colors.white.withOpacity(0.4),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.add_a_photo,
size: 50.0,
),
SizedBox(
height: 20,
),
Text(
'Add photo',
style: TextStyle(color: Colors.black, fontSize: 24),
),
],
),
),
),
]))),
const SizedBox(
height: 20,
),
Material(
child: SizedBox(
width: 200,
child: FilledButton(
onPressed: () async {
if (_currentImage.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Add photo pleasen!')));
return;
}
_goEditor(context);
},
child: const Text('Go Editor')),
),
),
const SizedBox(
height: 40,
),
],
),
);
}),
);
}
Future<void> _pickImage(BuildContext context) async {
ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image == null) {
return;
}
_currentImage = image?.path ?? '';
setState(() {});
}
Future<void> _goEditor(BuildContext context) async {
EditorUtil.goFluEditor(context,
orignal: _currentImage,
vipStatusCb: () {
debugPrint('get vip status: $isVipUser');
return isVipUser;
},
vipActionCb: () {
debugPrint('go Sub');
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return RoutePage(title: 'Sub page',);
},
));
},
saveCb: (path) async {
GallerySaver.saveImage(path, albumName: 'Flu-Editor');
},
loadWidgetCb: (islight, size, stroke) => Container(
width: size,
height: size,
alignment: Alignment.center,
child: CircularProgressIndicator(
color: islight ? Colors.white : Colors.black,
strokeWidth: stroke,
),
),
toastActionCb: (msg) => ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(msg))),
effectsCb: (page) async => await _fetchPF(),
saveEffectCb: (effect) async {
debugPrint('Save pf:${effect.toJson()}');
return await true;
},
deleteEffectCb: (id) async {
debugPrint('Delete:$id');
return await true;
},
filtersCb: () => _fetchLJ(),
stickersCb: () => _fetchStickers(),
fontsCb: () => _fetchFonts(),
framesCb: () => _fetchFrames(),
homeSavedCb: (context, after) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return RoutePage(savedPath: after, title: 'Saved page',);
},
));
});
}
Future<List<EffectData>> _fetchPF() async {
return await [
EffectData.fromJson({
'name': 'test',
'image':
'https://nwdnui.oss-cn-beijing.aliyuncs.com/user/effectSave/da2752d15d0e48359bbc42c7ec845d3d/1730962077026793.jpg',
'id': 0,
'params': jsonEncode({
"Brightness": 0.14719999999999997,
"Saturation": 1.0,
"Contrast": 1.0,
"Sharpen": 0.0,
"Shadow": 0.0,
"Temperature": 0.0,
"Noise": 0.0,
"Exposure": 0.0,
"Vibrance": 0.0,
"Highlight": 0.0,
"Red": 1.0,
"Green": 1.0,
"Blue": 1.0,
"CenterX": 0.5,
"CenterY": 0.5,
"Start": 1.0,
"End": 1.0
})
})
];
}
Future<List<FilterData>> _fetchLJ() async {
FilterDetail detail1 = FilterDetail();
detail1.id = 1;
detail1.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/90ad4f7bbd3243c285d4f8aaff5123be.jpg';
detail1.filterImage = 'luts/01-x.png';
detail1.name = 'class1';
detail1.noise = 0.2;
detail1.vip = 1;
detail1.lutFrom = 0;
FilterDetail detail2 = FilterDetail();
detail2.id = 2;
detail2.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/90ad4f7bbd3243c285d4f8aaff5123be.jpg';
detail2.filterImage = 'luts/03-x.png';
detail2.name = 'class2';
detail2.lutFrom = 0;
FilterData group1 = FilterData();
group1.groupName = 'class1';
group1.list = [detail1, detail2];
return [group1];
}
Future<List<StickerData>> _fetchStickers() async {
StickDetail detail1 = StickDetail();
detail1.id = 1;
detail1.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/e71b319ebce14952a87a40a03f8e7404.png';
detail1.name = 'sticker1';
detail1.vip = 0;
StickDetail detail2 = StickDetail();
detail2.id = 1;
detail2.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/1f0ceb1952a44a4ebd0a8c419a105545.png';
detail2.name = 'sticker2';
detail2.vip = 0;
StickerData group1 = StickerData();
group1.groupName = 'class1';
group1.groupImage =
'https://nwdnui.bigwinepot.com/ui/index/icon/318fa7a144af47f29adbdc73cb7e78b5.png';
group1.list = [detail1, detail2];
return [group1];
}
Future<List<FontsData>> _fetchFonts() async {
FontDetail detail1 = FontDetail();
detail1.id = 1;
detail1.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/ca9f5c3e742d49c2bafa28c8808a2280.jpg';
detail1.file =
'https://nwdnui.bigwinepot.com/ui/index/icon/7be3f3395e5c49b3aec36071c9bacc03.ttf';
detail1.name = 'font1';
detail1.vip = 0;
FontsData group1 = FontsData();
group1.groupName = 'Sample';
group1.list = [detail1];
return [group1];
}
Future<List<FrameData>> _fetchFrames() async {
FrameDetail detail1 = FrameDetail();
detail1.id = 1;
detail1.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/6c923546f7ff46d9bf613808b9bce72d.png';
detail1.name = 'frame1';
detail1.vip = 0;
FrameSize size = FrameSize();
size.frameWidth = 560;
size.frameHeight = 1000;
size.frameLeft = 94.0;
size.frameTop = 142.0;
size.frameRight = 88.0;
size.frameBottom = 114.0;
detail1.params = size;
FrameDetail detail2 = FrameDetail();
detail2.id = 2;
detail2.image =
'https://nwdnui.bigwinepot.com/ui/index/icon/e0ee85fe76e34fd093729428757e0401.png';
detail2.name = 'frame2';
detail2.vip = 0;
FrameSize size2 = FrameSize();
size2.frameWidth = 672;
size2.frameHeight = 1000;
size2.frameLeft = 136.0;
size2.frameTop = 154.0;
size2.frameRight = 136.0;
size2.frameBottom = 156.0;
detail2.params = size2;
FrameData group1 = FrameData();
group1.groupName = 'Sample';
group1.list = [detail1, detail2];
return [group1];
}
}
更多关于Flutter富文本编辑器插件flu_editor的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter富文本编辑器插件flu_editor的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
flu_editor
是一个用于 Flutter 的富文本编辑器插件,它提供了丰富的功能来编辑和格式化文本。使用 flu_editor
可以轻松地在 Flutter 应用中实现富文本编辑功能。
以下是如何在 Flutter 项目中使用 flu_editor
的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 flu_editor
的依赖:
dependencies:
flutter:
sdk: flutter
flu_editor: ^0.0.1 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 导入包
在你的 Dart 文件中导入 flu_editor
包:
import 'package:flu_editor/flu_editor.dart';
3. 使用 FluEditor
组件
你可以在你的 Flutter 应用中使用 FluEditor
组件来创建一个富文本编辑器。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:flu_editor/flu_editor.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FluEditor Example'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: FluEditor(
controller: FluEditorController(),
placeholder: 'Enter your text here...',
),
),
),
);
}
}
4. 使用 FluEditorController
FluEditorController
是 FluEditor
的核心控制器,用于管理编辑器的内容和状态。你可以使用它来获取或设置编辑器的内容,以及执行其他操作。
例如,你可以通过 FluEditorController
来获取当前的文本内容:
FluEditorController _controller = FluEditorController();
void _getText() {
String text = _controller.getText();
print(text);
}
5. 自定义工具栏
flu_editor
提供了一个默认的工具栏,但你可以自定义工具栏以满足你的需求。你可以通过 toolbarBuilder
参数来自定义工具栏:
FluEditor(
controller: _controller,
placeholder: 'Enter your text here...',
toolbarBuilder: (BuildContext context, FluEditorController controller) {
return Row(
children: [
IconButton(
icon: Icon(Icons.format_bold),
onPressed: () {
controller.toggleBold();
},
),
IconButton(
icon: Icon(Icons.format_italic),
onPressed: () {
controller.toggleItalic();
},
),
// 添加更多按钮...
],
);
},
)
6. 处理内容变化
你可以监听编辑器的内容变化,以便在用户输入时执行某些操作:
FluEditor(
controller: _controller,
placeholder: 'Enter your text here...',
onChanged: (String text) {
print('Text changed: $text');
},
)
7. 其他功能
flu_editor
还提供了许多其他功能,例如插入图片、链接、列表等。你可以查阅官方文档或源代码以了解更多详细信息。
8. 处理键盘事件
你可以通过 onKeyEvent
参数来处理键盘事件,例如在按下回车键时执行某些操作:
FluEditor(
controller: _controller,
placeholder: 'Enter your text here...',
onKeyEvent: (RawKeyEvent event) {
if (event.logicalKey == LogicalKeyboardKey.enter) {
print('Enter key pressed');
}
},
)
9. 样式定制
你可以通过 style
参数来定制编辑器的样式,例如字体大小、颜色等:
FluEditor(
controller: _controller,
placeholder: 'Enter your text here...',
style: TextStyle(
fontSize: 16,
color: Colors.black,
),
)