flu_editor
flu_editor 是一个用于 Flutter 应用的图像和视频颜色滤镜编辑插件,支持裁剪、颜色调整、滤镜、贴纸、文字和边框等多种编辑功能。
功能概述
插件目前已完成裁剪、颜色、滤镜、贴纸、文字和边框功能,模糊功能尚在开发中。它通过 EditorUtil 工具类提供页面导航和方法调用。
EditorUtil 工具类
主要方法 goFluEditor 用于跳转到编辑器页面,参数包括:
context:构建上下文,用于启动编辑器。orignal:原始图片路径。type:编辑类型(null 表示进入首页)。singleEditorSave:单独进入功能页面时,关闭是否保存图片到相册。vipStatusCb:回调函数,返回用户是否为 VIP 状态。vipActionCb:非 VIP 用户时触发跳转到订阅页面。saveCb:保存编辑后图片的回调,参数为保存路径。loadWidgetCb:加载提示回调,用于显示自定义加载动画。toastActionCb:显示自定义提示信息,如“保存成功”。effectsCb:获取并处理滤镜配方的回调。saveEffectCb:保存自定义滤镜配方。deleteEffectCb:删除已保存的滤镜配方。filtersCb:获取滤镜列表。stickersCb:获取贴纸列表。fontsCb:获取字体列表。framesCb:获取边框列表。homeSavedCb:编辑器主页保存图片后的回调。
内部页面路由支持直接跳转到特定功能区,包括裁剪、颜色调整、滤镜、贴纸、字体和相框编辑页面,但宿主应用应通过 goFluEditor 传入类型参数来调用。
多语言配置
在 Flutter 项目的 MaterialApp 中配置本地化代理和支持的语言环境,以确保插件文本正确显示:
MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
EditorLang.delegate
],
supportedLocales: [...EditorLang.delegate.supportedLocales],
)
使用示例
以下是一个简单的 Flutter 应用示例,展示如何集成并使用 flu_editor 进行图像编辑:
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _fluEditorPlugin = FluEditor();
String _currentImage = '';
bool isVipUser = false;
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [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: [
if (_currentImage.isNotEmpty) 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,
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 please!')));
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) {
_currentImage = image.path;
setState(() {});
}
}
Future<void> _goEditor(BuildContext context) async {
EditorUtil.goFluEditor(context,
orignal: _currentImage,
vipStatusCb: () => isVipUser,
vipActionCb: () {
debugPrint('go Sub');
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => 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 true;
},
deleteEffectCb: (id) async {
debugPrint('Delete:$id');
return true;
},
filtersCb: () => _fetchLJ(),
stickersCb: () => _fetchStickers(),
fontsCb: () => _fetchFonts(),
framesCb: () => _fetchFrames(),
homeSavedCb: (context, after) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => RoutePage(savedPath: after, title: 'Saved page'),
));
});
}
Future<List<EffectData>> _fetchPF() async {
return [
EffectData.fromJson({
'name': 'test',
'url': 'https://example.com/effect.jpg',
'id': 0,
'params': jsonEncode({
"Brightness": 0.1472,
"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()
..id = 1
..image = 'https://example.com/icon1.jpg'
..filterImage = 'luts/01-x.png'
..name = 'class1'
..noise = 0.2
..vip = 1
..lutFrom = 0;
FilterDetail detail2 = FilterDetail()
..id = 2
..image = 'https://example.com/icon2.jpg'
..filterImage = 'luts/03-x.png'
..name = 'class2'
..lutFrom = 0;
FilterData group1 = FilterData()
..groupName = 'class1'
..list = [detail1, detail2];
return [group1];
}
Future<List<StickerData>> _fetchStickers() async {
StickDetail detail1 = StickDetail()
..id = 1
..image = 'https://example.com/sticker1.png'
..name = 'sticker1'
..vip = 0;
StickDetail detail2 = StickDetail()
..id = 1
..image = 'https://example.com/sticker2.png'
..name = 'sticker2'
..vip = 0;
StickerData group1 = StickerData()
..groupName = 'class1'
..groupImage = 'https://example.com/group.png'
..list = [detail1, detail2];
return [group1];
}
Future<List<FontsData>> _fetchFonts() async {
FontDetail detail1 = FontDetail()
..id = 1
..image = 'https://example.com/font1.jpg'
..file = 'https://example.com/font1.ttf'
..name = 'font1'
..vip = 0;
FontDetail detail2 = FontDetail()
..id = 2
..image = 'https://example.com/font2.jpg'
..file = 'https://example.com/font2.ttf'
..name = 'font2'
..vip = 0;
FontsData group1 = FontsData()
..groupName = 'Sample'
..list = [detail1, detail2];
return [group1];
}
Future<List<FrameData>> _fetchFrames() async {
FrameDetail detail1 = FrameDetail()
..id = 1
..image = 'https://example.com/frame1.png'
..name = 'frame1'
..vip = 0
..params = FrameSize()
..frameWidth = 560
..frameHeight = 1000
..frameLeft = 94.0
..frameTop = 142.0
..frameRight = 88.0
..frameBottom = 114.0;
FrameDetail detail2 = FrameDetail()
..id = 2
..image = 'https://example.com/frame2.png'
..name = 'frame2'
..vip = 0
..params = FrameSize()
..frameWidth = 672
..frameHeight = 1000
..frameLeft = 136.0
..frameTop = 154.0
..frameRight = 136.0
..frameBottom = 156.0;
FrameData group1 = FrameData()
..groupName = 'Sample'
..list = [detail1, detail2];
return [group1];
}
}
依赖与许可证
插件依赖于多个 Flutter 包,如 flutter_bloc、image、extended_image 等,具体版本可参考项目文档。项目采用 MIT 许可证,详情请查看官方仓库。
通过 flu_editor,开发者可以快速集成强大的图像和视频编辑功能到 Flutter 应用中,提升用户体验。
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦