把 Flutter Server Box 移植到鸿蒙:一份完整的踩坑记录
芝麻蘸白糖
2026年08月22日 10:30
鸿蒙越用越香

背景

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 鸿蒙移植的同学。如果有问题,欢迎在评论区交流。