为了账号安全,请及时绑定邮箱和手机立即绑定

flu_editor Flutter插件:图像与视频编辑功能详解

标签:
Android 产品

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_blocimageextended_image 等,具体版本可参考项目文档。项目采用 MIT 许可证,详情请查看官方仓库。

通过 flu_editor,开发者可以快速集成强大的图像和视频编辑功能到 Flutter 应用中,提升用户体验。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消