背景
Server Box 是一款用 Flutter 开发的 Linux 服务器管理工具,支持 SSH 终端、SFTP、Docker 管理、系统监控等功能。原项目支持 Android、iOS、macOS、Linux、Windows 五端,本文记录将其移植到华为鸿蒙(OpenHarmony / HarmonyOS NEXT)平台的完整过程。
【在鸿蒙上原生运行serverBox】 在鸿蒙上原生运行serverBox
环境信息:
Flutter SDK:3.41.10-ohos-1.0.0(华为 fork,Dart 3.11.5)
DevEco Studio:内置 OHOS SDK
目标设备:OpenHarmony 6.1.1.120(API 24),aarch64
项目路径:flutter_server_box
一、环境准备
1.1 启用 OHOS 平台
华为 fork 的 Flutter 已经内置了 OHOS 平台支持,只需启用:
flutter config --enable-ohos
然后用 flutter create 补齐 OHOS 平台目录:
flutter create --platforms=ohos --org=com.example .
这会在项目下生成 ohos/ 目录,包含 entry/(主模块)、AppScope/、构建配置等。
1.2 环境变量
构建和运行时需要正确设置 OHOS SDK 相关的环境变量,关键是 hdc(鸿蒙的 adb 等价物)必须在 PATH 中,否则 Flutter 检测不到设备:
export HOS_SDK_HOME="/Applications/DevEco-Studio.app/Contents/sdk"
export DEVECO_SDK_HOME="/Applications/DevEco-Studio.app/Contents/sdk"
export JAVA_HOME="/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home"
export PATH=".../sdk/default/openharmony/toolchains:.../tools/hvigor/bin:.../tools/ohpm/bin:.../tools/node/bin:$flutter_sdk/bin:$PATH"
二、Dart 层兼容性修复
2.1 API 变更
Flutter 3.41 相比项目之前依赖的版本有几处 API 变更:
变更 文件 说明
SizeTransition.alignment → axisAlignment fl_lib 中 2 个文件 参数更名
sqlite3 close() → dispose() fl_lib sqlite.dart API 更名
onReorderItem → onReorder 6 个文件 回调参数更名
scrollCacheExtent 移除 1 个文件 参数删除
2.2 TargetPlatform.ohos 缺失
部分第三方库的 switch 语句枚举了所有 TargetPlatform 值但没有 ohos,导致编译报错。需要给以下库打补丁:
responsive_framework:responsive_utils.dart 中 switch 添加 TargetPlatform.ohos 分支
flutter_math_fork:selectable.dart 和 line_editable.dart 同理
flutter_secure_storage:添加 TargetPlatform.ohos 支持,复用 AndroidOptions 作为 fallback
2.3 Riverpod 生态断裂(最棘手的问题之一)
项目使用 riverpod 2.6.1 + riverpod_generator 2.6.4,但 build_runner 因为 dart_style 2.3.8 与 analyzer 6.x 不兼容而无法运行,意味着无法重新生成 .g.dart 文件。
而已有的 .g.dart 文件由 riverpod_generator 3.x 生成,引用的类型在 pinned 的 riverpod 2.6.1 中不存在。
解决方案: 手动重写所有 provider 定义,删除 16 个 .g.dart 文件,手写 NotifierProvider / NotifierProviderFamily:
// 非 Family provider
final snippetProvider = NotifierProvider<SnippetNotifier, List<Snippet>>(
SnippetNotifier.new,
);
// Family provider(多参数用 record)
final containerProvider =
NotifierProviderFamily<ContainerNotifier, List<Container>, (String, String, BuildContext)>(
ContainerNotifier.new,
);
涉及 16 个 provider 文件,是非 Family 11 个 + Family 5 个。
三、Rust FFI 交叉编译
项目通过 flutter_rust_bridge 将 Rust 代码暴露给 Dart,Rust 侧包含状态解析器(sbm_parser)和 FFI 绑定(sbm_ffi)。OHOS 的 Rust target aarch64-unknown-linux-ohos 是 Tier 3,需要 nightly + -Z build-std。
3.1 修改 native_toolchain_rust
native_toolchain_rust 有两处限制需要解除:
target 映射:config_mapping.dart 中 (OS.linux, Architecture.arm64) 原本映射到 aarch64-unknown-linux-gnu,改为 aarch64-unknown-linux-ohos
nightly 禁令:crate_info_validator.dart 的 deniedChannels 包含 nightly,移除之
3.2 链接器 wrapper
OHOS 的 ld.lld 不支持 GNU ld 的一些选项(--version-script、--as-needed、--gc-sections、-Bstatic 等),需要写一个 wrapper 脚本过滤这些标志:
#!/bin/bash
# ohos-clang-wrapper.sh
args=()
for arg in "$@"; do
case "$arg" in
--version-script=*|--as-needed|--gc-sections|--eh-frame-hdr) ;;
--strip-all|-Bstatic|-Bdynamic) ;;
-z) shift; continue ;;
*) args+=("$arg") ;;
esac
done
exec /path/to/ohos/clang --target=aarch64-linux-ohos "${args[@]}"
3.3 flutter_pty 的 C 代码编译
flutter_pty 包通过 native_toolchain_c 的 CBuilder 编译 C 代码,但 CBuilder 不认识 OHOS target。解决方案是绕过 CBuilder,直接调用 OHOS clang 编译并手动添加 CodeAsset。
3.4 构建配置
在项目的 hook/build.dart 中通过 extraCargoEnvironmentVariables 传递所有 OHOS 相关的环境变量(CC、CXX、AR、LINKER、CFLAGS 等),通过 extraCargoBuildArgs 传递 -Z build-std。
四、原生插件缺失(白屏根因)
构建出 HAP 后安装到设备,应用白屏。通过在 main() 中添加 try-catch 调试输出,定位到 _initApp() 抛异常,runApp() 从未被调用。
4.1 三个缺失的 MethodChannel 插件
OHOS 平台的 Flutter 引擎不会自动注册 Android/iOS 的原生插件。以下三个插件在 OHOS 上没有实现:
插件 用途 解决方案
path_provider 获取应用文件路径 在 EntryAbility.ets 中注册 MethodChannel,返回 OHOS 的 getApplicationContext().filesDir 等
shared_preferences 键值存储 在 EntryAbility.ets 中用内存 Map 实现
flutter_secure_storage 加密存储 同上,内存 Map 实现
在 EntryAbility.ets 的 configureFlutterEngine 中注册:
class PathProviderHandler {
onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
case 'getApplicationDocumentsPath':
result.success(this.context.filesDir);
break;
case 'getTemporaryPath':
result.success(this.context.tempDir);
break;
// ...
}
}
}
4.2 SQLite 库缺失(最终 Boss)
修复上述三个插件后,应用仍然白屏。错误信息:
Unsupported operation: Unsupported platform: ohos
#0 defaultOpen (package:sqlite3/src/ffi/loadlibrary.dart:103)
问题分析:
sqlite3-2.9.4 包的 _defaultOpen() 只处理 Android、iOS、macOS、Windows、Linux,不处理 OHOS
其他平台用系统 SQLite(Linux/macOS)或系统自带(Android),但 OHOS 没有系统 SQLite 库
HAP 中也没有打包 libsqlite3.so
sqlite3-2.9.4 没有 hook/build.dart,无法通过 native assets 机制自动构建
解决方案:手动编译 SQLite3MultipleCiphers 源码
项目使用 sqlite3mc(SQLite3MultipleCiphers)以支持加密。从 GitHub releases 下载 amalgamation 源码,用 OHOS clang 编译:
OHOS_CLANG=".../openharmony/native/llvm/bin/clang"
OHOS_SYSROOT=".../openharmony/native/sysroot"
$OHOS_CLANG \
--target=aarch64-linux-ohos \
--sysroot=$OHOS_SYSROOT \
-O2 -fPIC -shared \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE3MC_ENABLE_INTEGRATED_CRYPTO=1 \
# ... 其他 defines
sqlite3mc_amalgamation.c \
-o libsqlite3.so \
-lm -ldl -lpthread
编译产物放到 ohos/entry/libs/arm64-v8a/libsqlite3.so,OHOS 构建系统会自动打包进 HAP。
然后修改 sqlite3 包的 load_library.dart,在 _defaultOpen() 中添加 OHOS 分支:
} else if (Platform.operatingSystem == 'ohos') {
return DynamicLibrary.open('libsqlite3.so');
}
五、最终结果
重新构建 HAP 并安装:
flutter build hap --release
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell "aa start -a EntryAbility -b com.example.server_box"
应用成功启动,显示 ServerBox 主界面:顶部标题栏、空服务器列表(显示"空")、底部五栏导航(伺服器/终端/文件/代码/AI)、右下角添加按钮。白屏问题彻底解决。
六、踩坑总结
6.1 问题分类
类别 问题数 典型问题
Dart API 变更 4 类 参数更名、枚举缺失
代码生成断裂 1 riverpod .g.dart 无法重新生成
Rust 交叉编译 4 target 映射、nightly 限制、链接器不兼容、C 编译器不识别 target
原生插件缺失 4 path_provider、shared_preferences、secure_storage、sqlite3
6.2 经验教训
白屏不一定是渲染问题:Flutter 在 OHOS 上白屏,根因是 _initApp() 中的初始化异常导致 runApp() 没有执行。在 main() 中加 try-catch 是最快的定位手段。
pub cache 里的补丁不可持久:修改 pub cache 中的第三方库只是临时方案,flutter pub cache repair 或重新拉取就会丢失。正式做法应该是 dependency_overrides 指向本地 fork。
OHOS ≠ Linux:虽然 OHOS 底层是 Linux 内核,但 Platform.operatingSystem 返回 "ohos" 而非 "linux",很多库的 Platform.isLinux 判断不会命中。Rust target 也是 aarch64-unknown-linux-ohos 而非 aarch64-unknown-linux-gnu,sysroot 和工具链都不同。
native assets 机制依赖包自身的 hook:pubspec.yaml 里的 hooks: user_defines 配置只有当包自身有 hook/build.dart 时才生效。sqlite3-2.9.4 没有 hook,所以这个配置实际上是空转的——在其他平台靠系统库蒙混过关,到 OHOS 就露馅了。
链接器差异是最隐蔽的坑:GNU ld 和 OHOS 的 ld.lld 选项不兼容,但报错信息往往不直观。写一个 wrapper 脚本过滤不支持的选项是最务实的做法。
6.3 待办
将 pub cache 中的补丁迁移为本地 fork + dependency_overrides
shared_preferences 和 flutter_secure_storage 目前用内存 Map 实现,重启后数据丢失,需要接入 OHOS 持久化存储
CI 跨平台编译验证
sqlite3 升级到 3.5.2(有原生 hook,可自动构建)
写在最后
Flutter 的鸿蒙移植整体上是可行的,但生态适配的工作量不小。主要阻力来自两个方面:一是第三方库对 OHOS 平台的无感知(TargetPlatform.ohos 缺失、Platform.operatingSystem 不匹配),二是原生层差异(无系统 SQLite、链接器选项不兼容、Rust target 是 Tier 3)。
华为 fork 的 Flutter SDK 本身做得不错,flutter build hap 能直接用,EntryAbility.ets 的 MethodChannel 机制也和 Android 侧基本对齐。只要把上述生态缺口补上,Flutter 应用上鸿蒙是完全可以走通的。
希望这份记录能帮到同样在做 Flutter 鸿蒙移植的同学。如果有问题,欢迎在评论区交流。