已有 Flutter 应用想覆盖鸿蒙设备,最先要解决的不是 Widget 怎么写,而是用哪套 Flutter 工具链、目标设备运行什么系统、依赖的插件有没有对应平台实现。纯 Dart 界面通常容易复用,摄像头、WebView、定位、文件选择等能力则必须逐个验证。

本文从零跑通一条开发链:选择适配版 Flutter → 配置 DevEco 与 SDK → 生成 ohos/ 工程 → 真机调试 → 构建 HAP → 接入 ArkTS 能力 → 检查插件与发布质量。

资料基线(2026-10-03):本文的命令以 OpenHarmony-SIG flutter_flutter 仓库及其示例文档为依据。该仓库 README 使用 API 12、DevEco Studio 5.0、JDK 17 作为一个文档基线;版本与设备兼容关系会变化,实际项目应固定并验证同一套 Flutter fork、Engine、DevEco、SDK 和插件版本。本文不把 OpenHarmony-SIG 社区适配视为 Flutter 上游的官方部署平台。


一、先分清三个名字

名称在本文中的角色开发时要记住什么
Flutter 上游Flutter 官方 SDK 与工具链官方支持平台列表包含 Android、iOS、Web、桌面等;没有把 ohos 列为官方部署目标
OpenHarmony开源操作系统及其 API/SDK社区适配项目以 ohos 作为 Flutter 平台标识
HarmonyOS / 鸿蒙设备具体商业设备与系统发行版设备版本、API、签名和插件能力要按实际型号验证,不能仅凭“鸿蒙”二字推断兼容

OpenHarmony-SIG 的 flutter_flutter 在 Flutter SDK 基础上扩展了工程模板、Flutter Tools、Engine 与平台嵌入层,使 Dart UI 能在目标设备上运行。其命令仍叫 flutter,但必须确认终端调用的是适配版:官方上游 SDK 的 flutter create --platforms ohos 通常不会识别 ohos。

Dart 业务与 Widget

Flutter Framework

OpenHarmony 适配的 Flutter Engine

ArkTS / 原生嵌入层

OpenHarmony / 目标鸿蒙设备

MethodChannel / EventChannel

ohos 插件实现

这也解释了迁移难度的分布:布局、动画与大部分 Dart 业务逻辑主要依赖 Flutter;平台插件越多,对 ohos 的适配工作就越多。


二、迁移前先做一次依赖盘点

把 pubspec.yaml 中的依赖分成三类:

依赖类型例子主要检查点
纯 Dart 包JSON、日期、状态管理、网络协议层是否依赖特定平台文件路径、FFI 动态库或浏览器 API
Flutter UI 包常规 Widget、绘制、动画字体、输入法、无障碍、窗口尺寸与性能是否一致
平台插件相机、定位、WebView、推送、支付、文件选择是否存在 ohos 实现;版本是否与当前 Flutter fork 匹配

不要把“某插件名字出现在已适配清单中”理解为“任意新版本都能直接使用”。OpenHarmony-SIG 的插件表往往标明适配的上游基线版本,而上游插件继续升级后,Dart 接口和原生实现可能已经变化。先锁定应用实际依赖版本,再找匹配的适配仓库或自行补齐实现。

还要区分“插件已编译”和“功能已验收”:权限弹窗、后台生命周期、文件 URI、相机预览层、WebView 登录态都可能在运行时才暴露问题。


三、准备工具链:先让 flutter doctor 看见 OpenHarmony

3.1 安装清单

按 OpenHarmony-SIG 文档基线准备:

  1. DevEco Studio 与对应设备 API 的 OpenHarmony/HarmonyOS SDK;
  2. JDK 17;
  3. Git 和可访问的 Dart/pub、Engine 依赖下载源;
  4. OpenHarmony-SIG 的 flutter_flutter 适配版;
  5. 一台开启开发者模式并可通过 hdc 连接的设备,或文档支持的模拟器。

文档基线列出 API 12、DevEco Studio 5.0。新设备若使用更高 API,不应直接假定这套老组合可用;先查看 fork 分支、Engine 产物和设备发行版的兼容说明,再决定升级。

3.2 获取适配版 Flutter

git clone https://gitee.com/openharmony-sig/flutter_flutter.git
cd flutter_flutter

# 示例:仓库中确实存在的发布分支;实际项目应选择已验证的版本并固定提交
git checkout 3.7.12-ohos-1.0.4

export PATH="$PWD/bin:$PATH"
command -v flutter
flutter --version
flutter doctor -v

这个分支名用来展示“固定版本”的做法,不是所有鸿蒙设备的通用推荐版本。仓库还存在其他适配分支;不要只看 Flutter 版本号,还要核对与之配套的 Engine、DevEco、SDK 和插件。团队应把选定的提交 SHA、SDK/API 版本和构建镜像记录下来,避免开发机与 CI 使用不同组合。

如果 command -v flutter 指向电脑上原有的官方 Flutter 路径,就先调整 PATH,再检查 flutter --version。不同 SDK 共存时,最常见的错误是“命令能运行,但运行的是另一套 Flutter”。

3.3 DevEco 环境变量

以 macOS 文档中的目录结构为例,按本机实际安装路径调整:

export DEVECO_SDK_HOME="/Applications/DevEco-Studio.app/Contents/sdk"
export PATH="/Applications/DevEco-Studio.app/Contents/tools/ohpm/bin:$PATH"
export PATH="/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin:$PATH"
export PATH="/Applications/DevEco-Studio.app/Contents/tools/node/bin:$PATH"
export PATH="$DEVECO_SDK_HOME/default/openharmony/toolchains:$PATH"

java -version
ohpm --version
hdc list targets
flutter doctor -v

DevEco 或 command-line-tools 的安装目录可能不同,SDK 目录也可能按版本组织;以本机安装结果为准。flutter doctor -v 应能识别 Flutter 与 OpenHarmony 相关环境。如果设备未列出,先排查数据线、开发者模式、驱动与 hdc,不要急着改 Dart 代码。

下载源不可达时,可按团队网络环境配置 PUB_HOSTED_URL 与 FLUTTER_STORAGE_BASE_URL。改变 Engine 下载源后,旧缓存可能与新的 Dart/Engine 版本不匹配,需按适配仓库 FAQ 清理相应缓存并重新构建;不要在日常开发中随意删除整个 SDK 缓存。


四、创建第一个 ohos 工程并运行

确认当前终端使用适配版 Flutter 后:

flutter create --platforms ohos --org com.example hello_ohos
cd hello_ohos
flutter pub get
flutter devices
flutter run --debug -d <device-id>

<device-id> 替换为 flutter devices 或 hdc list targets 显示的设备 ID。第一次运行会下载依赖、编译原生模块,通常比热重载慢。连接真机前还要在 DevEco 中完成适用于该设备的调试签名配置;签名失败属于原生构建/部署阶段,不能靠修改 Widget 解决。

生成的项目仍以 lib/main.dart 为 Dart 入口,但多出 ohos/ 原生工程:

hello_ohos/
├── lib/
│   └── main.dart          # Flutter UI 与业务代码
├── pubspec.yaml           # Dart/Flutter 依赖与资源
└── ohos/
    ├── entry/             # 应用入口模块、ArkTS 代码与资源
    ├── build-profile.json5
    └── oh-package.json5   # 原生依赖配置

实际生成目录会随适配版变化;以仓库模板为准。ohos/ 不只是可随手删除的构建缓存,它保存了签名、权限、包名和原生插件接入配置,应纳入版本管理。

最小 Dart 页面

下面的页面本身没有鸿蒙专属 API,适合先验证基础渲染与交互:

import 'package:flutter/material.dart';

void main() => runApp(const DemoApp());

class DemoApp extends StatefulWidget {
  const DemoApp({super.key});

  
  State<DemoApp> createState() => _DemoAppState();
}

class _DemoAppState extends State<DemoApp> {
  int count = 0;

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Flutter on OpenHarmony')),
        body: Center(child: Text('点击次数:$count')),
        floatingActionButton: FloatingActionButton(
          onPressed: () => setState(() => count++),
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

页面正常显示,只证明 Flutter Framework、Engine 与输入事件的基础路径通了。接下来才轮到网络、存储、权限和原生插件验收。


五、打包:HAP 与 APP 分别做什么

# 调试构建
flutter build hap --debug

# 发布构建
flutter build hap --release

# 适配版文档提供的应用包构建命令
flutter build app --release

HAP 是可以安装的应用模块包;适配版 README 给出的典型产物位置是 ohos/entry/build/default/outputs/default/entry-default-signed.hap。实际文件名和是否带 signed 取决于配置与构建类型。APP 是面向应用分发的包格式,签名、包名、版本和上架要求仍需按目标渠道检查。

已连接设备时可以通过工具链安装:

flutter devices
hdc -t <device-id> install <hap-file-path>

不要把 Debug 包能启动等同于 Release 可交付。至少在真实设备上跑一次 Release:部分引擎资源、插件注册、混淆或签名问题只有在发布构建中出现。


六、Flutter 怎样调用鸿蒙原生能力

纯 UI 可以继续写在 Dart;需要读取设备信息、调系统能力或使用原生 SDK 时,通过 MethodChannel、EventChannel 或插件把调用交给 ArkTS/原生层。

6.1 Dart 侧发起一次请求

import 'package:flutter/services.dart';

class DeviceBridge {
  static const MethodChannel _channel =
      MethodChannel('example.dev/device');

  static Future<String> platformName() async {
    return await _channel.invokeMethod<String>('getPlatformName')
        ?? 'unknown';
  }
}

Channel 名称和方法名必须与原生侧完全一致。请求返回的是 Future,调用方应处理平台不可用、权限拒绝和超时等异常。

6.2 ArkTS 侧接收请求

OpenHarmony-SIG 的 channel_demo 展示了插件在 onAttachedToEngine 中使用 MethodChannel 与 FlutterPluginBinding。下面只保留关键逻辑;实际 import、插件注册和生命周期方法应以所选适配版模板为准:

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(
    binding.getBinaryMessenger(),
    'example.dev/device'
  );

  this.channel.setMethodCallHandler({
    onMethodCall(call: MethodCall, result: MethodResult): void {
      if (call.method === 'getPlatformName') {
        result.success('OpenHarmony');
      } else {
        result.notImplemented();
      }
    }
  });
}

这是示意片段,不是可独立编译的完整 ArkTS 插件。真正接设备 API 时,还要处理权限声明、错误映射、UI 线程约束以及引擎解绑时的清理。如果是持续推送位置或传感器数据,使用 EventChannel 比反复调用 MethodChannel 更贴近事件流语义。

6.3 什么时候做成插件

只有一个页面需要少量原生调用,可以先在宿主工程里验证 Channel。多个业务模块或多个 Flutter 项目会复用时,把能力封装成带 ohos 实现的 Flutter 插件更容易维护。适配版支持 flutter create -t plugin --platforms ohos,android,ios 生成多平台插件模板,但原生实现仍需要自己写。


七、插件兼容是迁移成本的主体

pub.dev 上的 Flutter 插件通常只承诺其声明的平台实现。一个插件在 Android 与 iOS 正常,并不说明它有 ohos 实现。OpenHarmony-SIG 提供过插件适配清单;原 Gitee flutter_packages 仓库已标注归档,并指向 GitCode 上的新地址,查插件时应先看新仓库和具体插件项目的维护状态。

逐个插件按这个顺序检查:

  1. pubspec.lock 锁定的插件版本是什么?
  2. 插件是纯 Dart、平台接口,还是含 Android/iOS 原生实现?
  3. 是否存在与该版本匹配的 ohos 实现和实际设备测试记录?
  4. 原生权限、生命周期与系统 UI 行为是否和业务要求一致?
  5. 如果缺失,实现替代方案、自己补插件或缩小功能范围,成本各是多少?

例如 path_provider、image_picker、url_launcher、webview_flutter 等在适配清单中可以找到对应工作,但要按清单的基线版本核对,而不是直接把应用中的最新版覆盖过去。对 FFI 插件,还需确认目标 ABI、动态库格式和系统 API,而不是只看 Dart 层是否能解析依赖。

迁移时避免把整个 pubspec.yaml 一次性升级到最新:先固定已能运行的 Flutter fork,再逐个替换插件并做真机回归。对于临时 fork,记录 Git 提交 SHA,避免仓库分支移动造成不可复现的构建。


八、从现有 Flutter App 迁移时怎么拆任务

建议按“最小可运行 + 依赖分层”推进:

阶段交付物验证重点
1. 工具链空白 ohos App 可安装、启动版本、签名、设备识别
2. 纯 Dart UI首页与核心流程可浏览布局、字体、输入法、路由、屏幕适配
3. 平台插件每个插件独立 demo权限、生命周期、错误处理、Release 构建
4. 数据与登录本地存储、网络、账号状态沙箱路径、证书、登录回跳、数据恢复
5. 性能与发布真实设备矩阵与正式包冷启动、滚动、内存、崩溃、签名和包体积

若已有鸿蒙原生 ArkTS 应用,也可以用 Flutter module 做混合开发,而不是把整个 App 改成 Flutter。OpenHarmony-SIG 文档提供了 module、FlutterPage、FlutterEntry 和多引擎示例。混合模式需要额外设计路由边界、引擎生命周期和两个 UI 技术栈之间的状态同步。

发布前尤其容易漏的测试

  • 冷启动、热启动、退后台与进程重建;
  • 系统返回手势、软键盘弹出与收起、横竖屏与窗口尺寸;
  • 权限拒绝后再次申请;相机、相册、文件选择返回路径;
  • WebView 登录、支付/分享回跳、深链;
  • 弱网、无网和离线缓存;
  • Debug 与 Release 在同一设备上的功能差异;
  • 低内存和长时间使用后的泄漏、掉帧与电量。

九、几个常见故障如何定位

--platforms ohos 不识别

先运行 command -v flutter 与 flutter --version。通常是终端调用了上游 Flutter SDK,或 IDE 仍指向另一套 SDK。切换到 OpenHarmony-SIG 适配版后重开终端与 IDE,再执行 flutter doctor -v。

flutter doctor 找不到 OpenHarmony 工具链

检查 DevEco、SDK、JDK 17、ohpm、hvigor 和 hdc 是否位于实际安装目录。只设置了 Flutter 的 PATH,不代表原生工具链已经可用。

Debug 能跑,Release 失败

先比较签名配置、插件注册与 Engine 资源;确认 Debug 与 Release 使用的是同一套适配版与下载源。不要在没有证据时清空所有缓存,先看构建日志中失败的模块和首个异常。

插件能编译,调用时报 MissingPluginException

检查该插件是否真的包含 ohos 实现、是否注册到当前 Engine、插件版本是否与适配版兼容。多引擎或混合开发场景要特别检查插件是否在每个 Engine 上完成注册。

HAP 安装失败

先排除设备 API 不匹配、签名证书、包名冲突、设备架构与安装权限问题。hdc 的安装错误和系统日志通常比 Flutter UI 异常更接近原因。


十、结语:复用 Flutter 代码,也要承担平台适配

Flutter 开发鸿蒙的可行路径已经很明确:使用社区适配的 SDK 和 Engine,生成 ohos/ 工程,在 DevEco 工具链下构建 HAP,再通过 ArkTS 插件接入平台能力。真正决定项目成本的,是现有插件清单、设备 API 版本和发布质量要求。

先拿一台目标设备做空工程,再带入业务页面,再逐个验证插件。做到“Dart 代码能运行”只是起点;签名、权限、生命周期、原生能力和真机性能通过回归后,才算真正完成一个鸿蒙版本。

参考资料

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐