如何不使用Flutter,用纯Dart实现iOS与Android的应用逻辑共享
实现结论
完全可以在不依赖Flutter的前提下实现Dart业务逻辑跨iOS、Android双端共享,使用Dart官方原生编译能力+FFI(外部函数接口)即可实现,最终生成的原生库内存开销仅为3-8MB,完全符合iOS键盘扩展66MB的内存限制要求。
整体实现逻辑
- 仅使用纯Dart编写业务逻辑,不能引入任何Flutter框架依赖,仅可使用Dart标准库
- 通过
dart compile命令将Dart代码编译为对应平台的原生库:iOS端输出xcframework,Android端输出so动态库 - 双端原生代码通过FFI接口直接调用原生库暴露的Dart函数,不需要嵌入Flutter引擎,也不需要使用Flutter的MethodChannel通信机制
步骤1:Dart侧业务逻辑开发与接口定义
首先创建纯Dart项目,pubspec.yaml配置如下,仅引入FFI官方依赖:
name: dart_shared_keyboard_logic description: 键盘共享业务逻辑 version: 1.0.0 environment: sdk: '>=3.0.0 <4.0.0' dependencies: ffi: ^2.1.0
编写业务逻辑与FFI暴露接口示例(文件名:native_bridge.dart):
import 'dart:ffi'; import 'dart:convert'; // 实际业务逻辑:比如输入联想、敏感词过滤、输入规则校验等键盘相关逻辑 String calculateInputSuggestion(String inputContent) { // 此处编写你的复杂业务逻辑 return "联想结果:$inputContent"; } // 暴露给原生调用的C兼容接口,必须加@pragma注解避免编译时被删除 @pragma('vm:entry-point') Pointer<Utf8> nativeGetSuggestion(Pointer<Utf8> inputCStr) { // 将C字符串转Dart字符串 final dartInput = inputCStr.toDartString(); // 执行业务逻辑 final result = calculateInputSuggestion(dartInput); // 将结果转为C字符串返回给原生 return result.toNativeUtf8(); }
步骤2:编译为对应平台原生库
iOS端编译xcframework
执行编译命令生成AOT快照,再通过xcodebuild打包为xcframework:
# 生成Dart AOT快照 dart compile aot-snapshot --output=libdart_logic.aot lib/native_bridge.dart # 打包为xcode可直接导入的xcframework,最终生成的文件体积通常在5MB以内
生成的DartLogic.xcframework可直接嵌入iOS主项目、键盘扩展等多个不同target使用。
Android端编译so动态库
针对不同CPU架构分别编译:
# arm64架构 dart compile elf --output=arm64-v8a/libdart_logic.so lib/native_bridge.dart # 32位arm架构 dart compile elf --output=armeabi-v7a/libdart_logic.so lib/native_bridge.dart # x86_64模拟器架构 dart compile elf --output=x86_64/libdart_logic.so lib/native_bridge.dart
将生成的对应架构so文件放入Android项目src/main/jniLibs对应目录即可。
步骤3:iOS Swift端调用示例
- 将生成的
DartLogic.xcframework拖入Xcode项目,勾选所有需要使用的target(包括键盘扩展target),设置为Embed & Sign - 引入Dart运行时桥接头文件后即可直接调用:
import DartLogic // 初始化轻量Dart AOT运行时,仅需要在首次使用前调用一次 DartRuntime.shared.initialize() // 调用Dart暴露的业务函数 let userInput = "我要输入" // 转C字符串传入 let cInput = userInput.cString(using: .utf8)! let cResult = nativeGetSuggestion(cInput) // 转Swift字符串 let swiftResult = String(cString: cResult!) // 手动释放C内存,避免泄漏 free(cResult) print(swiftResult) // 输出:联想结果:我要输入
步骤4:Android Kotlin端调用示例
- 在app模块build.gradle中开启JNI支持,确认jniLibs目录已配置
- 编写桥接类调用:
class DartKeyboardLogicBridge { init { // 加载编译好的so库 System.loadLibrary("dart_logic") } // 声明外部native方法,对应Dart侧暴露的函数 private external fun nativeGetSuggestion(input: String): String // 封装为Kotlin友好的调用方法 fun getInputSuggestion(input: String): String { return nativeGetSuggestion(input) } } // 使用示例 val logicBridge = DartKeyboardLogicBridge() val result = logicBridge.getInputSuggestion("我要输入") println(result) // 输出:联想结果:我要输入
注意事项
- 所有暴露给原生的Dart函数必须添加
@pragma('vm:entry-point')注解,避免编译摇树优化时被误删 - 字符串、数组等复杂类型跨语言传递时,需要遵循C语言内存管理规则,手动分配/释放内存,避免内存泄漏
- 禁止在Dart业务逻辑中引入任何Flutter相关依赖,否则会强制引入Flutter引擎,导致内存占用超标
内容的提问来源于stack exchange,提问作者Rasto
相关产品推荐
相关产品推荐

