Flutter 创建一个插件(FFI)

1、新建项目 (streaming_writing_to_png)

js 复制代码
flutter create --org com.company.streamingwritingtopng --template=plugin_ffi --platforms=android,ios streaming_writing_to_png

2、将代码写到src目录下

当我们执行完第一步的命令之后会生成一个项目,里面有1个src文件夹,里面是需要输入我的的C语言代码的.

例子

我的目录结构

css 复制代码
src/
├── libspng/
│   ├── spng.h
│   └── spng.c
├── streaming_writing_to_png.h
└── streaming_writing_to_png.c
js 复制代码
// src/streaming_writing_to_png.h

#include <stdint.h>

#if _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FFI_PLUGIN_EXPORT
#endif

FFI_PLUGIN_EXPORT int32_t start_png_export(const char* file_path, uint32_t width, uint32_t height);

FFI_PLUGIN_EXPORT int32_t write_png_chunk(uint8_t* chunk_data);

FFI_PLUGIN_EXPORT int32_t finish_png_export(void);
js 复制代码
// src/streaming_writing_to_png.c

#include "streaming_writing_to_png.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libspng/spng.h>

  
typedef struct PNGStreamWriter {
    // code...
} PNGStreamWriter;

PNGStreamWriter* writer = NULL;

// 释放资源
void png_stream_writer_free(PNGStreamWriter* writer) {
    // code...
}

// 写入一行像素数据 (RGBA格式,每像素4字节)
int png_stream_writer_write_row(PNGStreamWriter* writer, const uint8_t* row_data) {
    // code...
}

// 完成编码(验证是否写入了所有行)
int png_stream_writer_finish(PNGStreamWriter* writer) {
    // code...
}

// 静态回调函数用于写入数据
static int write_callback(spng_ctx* ctx, void* user, void* data, size_t length) {
    // code...
}

// 创建 PNGStreamWriter 对象
PNGStreamWriter* png_stream_writer_new(const char* filename, uint32_t width, uint32_t height) {
    // code...
}

// 获取当前行号
uint32_t png_stream_writer_get_current_row(PNGStreamWriter* writer) {
    // code...
}

  
// 获取总行数
uint32_t png_stream_writer_get_total_rows(PNGStreamWriter* writer) {
    // code...
}

// 便捷函数:写入行数据(带长度检查)
int png_stream_writer_write_row_checked(PNGStreamWriter* writer, const uint8_t* row_data, size_t data_size) {
    // code...
}


// -------------------------------------------------------------
// FFI 接口 1: 初始化(开始 PNG 编码)
// -------------------------------------------------------------
int32_t start_png_export(const char* file_path, uint32_t width, uint32_t height) {
    // code...
}

// -------------------------------------------------------------
// FFI 接口 2: 写入数据块
// -------------------------------------------------------------
// 接收当前数据块的指针和字节大小
int32_t write_png_chunk(uint8_t* chunk_data) {
    // code...
}

// -------------------------------------------------------------
// FFI 接口 3: 完成(写入 PNG 尾部和清理)
// -------------------------------------------------------------
int32_t finish_png_export(void) {
    // code...
}

3、生成函数绑定

js 复制代码
dart run ffigen --config ffigen.yaml

执行完上面语句之后就生成绑定到lib/streaming_writing_to_png_bindings_generated.dart需要提前执行flutter pub add ffigen

4、测试

example/lib/main.dart 编写测试程序进行测试。

5、会遇到的问题

我们使用到了一个libspng的开源库,因此我们要做以下处理:

5.1 在 ios/Classes/streaming_writing_to_png.c``#include "../../src/libspng/spng.c"

5.2 修改 ios/streaming_writing_to_png.podspec 文件

js 复制代码
Pod::Spec.new do |s|
    s.name = 'streaming_writing_to_png'
    s.version = '0.0.1'
    s.summary = 'A new Flutter FFI plugin project.'
    s.description = <<-DESC
    A new Flutter FFI plugin project.
    DESC
    s.homepage = 'http://example.com'
    s.license = { :file => '../LICENSE' }
    s.author = { 'Your Company' => 'email@example.com' }

    # This will ensure the source files in Classes/ are included in the native
    # builds of apps using this FFI plugin. Podspec does not support relative
    # paths, so Classes contains a forwarder C file that relatively imports
    # `../src/*` so that the C sources can be shared among all target platforms.
    s.source = { :path => '.' }
    # 新增了'../src/**/*.{h,c}'
    s.source_files = 'Classes/**/*', '../src/**/*.{h,c}' 
    # 本行为新增
    s.public_header_files = '../src/**/*.h' 
    s.dependency 'Flutter'
    s.platform = :ios, '11.0'
    s.libraries = 'z' # 本行为新增,添加zip依赖

    # Flutter.framework does not contain a i386 slice.
    s.pod_target_xcconfig = {
    # 本行为新增
    'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/../src"', 
    'DEFINES_MODULE' => 'YES',
    'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
    # 本行为新增
    'GCC_PREPROCESSOR_DEFINITIONS' => 'SPNG_STATIC=1' 
    }
    s.swift_version = '5.0'
end

5.3 完结,撒花🎉

相关推荐
奋斗的小青年!!1 天前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘1 天前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!1 天前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者961 天前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei1 天前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei1 天前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!1 天前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_1 天前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter