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

相关推荐
程序员Ctrl喵2 天前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
前端不太难2 天前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡2 天前
flutter列表中实现置顶动画
flutter
始持2 天前
第十二讲 风格与主题统一
前端·flutter
始持2 天前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持2 天前
第十三讲 异步操作与异步构建
前端·flutter
新镜2 天前
【Flutter】 视频视频源横向、竖向问题
flutter
黄林晴2 天前
Compose Multiplatform 1.10 发布:统一 Preview、Navigation 3、Hot Reload 三箭齐发
android·flutter
Swift社区2 天前
Flutter 应该按功能拆,还是按技术层拆?
flutter
肠胃炎2 天前
树形选择器组件封装
前端·flutter