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 完结,撒花🎉

相关推荐
木易 士心10 小时前
Flutter - dart 语言从入门到精通
flutter
文阿花21 小时前
flutter 3.22+ Android集成高德Flutter地图自定义Marker显示
android·flutter
猪哥帅过吴彦祖1 天前
Flutter 系列教程:布局基础 (上) - `Container`, `Row`, `Column`, `Flex`
前端·flutter·ios
星海浮沉1 天前
flutter AudioPlayer的使用问题及处理
flutter
-晨-风-1 天前
Flutter 运行IOS真机,提示无法打开“****”
flutter·ios
zhifanxu1 天前
flutter mixin
flutter
zhifanxu1 天前
Flutter开发学习汇总
flutter
猪哥帅过吴彦祖2 天前
Flutter 系列教程:核心概念 - StatelessWidget vs. StatefulWidget
前端·javascript·flutter