Flutter Module 集成到 HarmonyOS 原生应用指南

1. 概述

Flutter Module(flutter_module)集成到 HarmonyOS 原生应用(flutter_Ohos)的完整过程,采用 单 Ability + FlutterEntry 架构:

  • 应用只有一个 EntryAbility(继承 UIAbility),首页为原生 ArkUI 页面
  • 通过 router.pushUrl 跳转到 Flutter 页面,使用 FlutterEntry 创建引擎并渲染
  • 支持传递不同 route 参数,直接打开指定的 Flutter 页面

架构示意

EntryAbility (UIAbility)
  └── pages/Index(原生首页 · 按钮列表)
        │
        ├── router.pushUrl({ params: { route: '/' } })
        ├── router.pushUrl({ params: { route: '/counter' } })
        ├── router.pushUrl({ params: { route: '/profile' } })
        └── router.pushUrl({ params: { route: '/settings' } })
              │
              └── pages/FlutterIndex
                    └── FlutterEntry(读取 route → 创建 FlutterEngine → 渲染 FlutterPage)

2. 前置条件

依赖项 说明
DevEco Studio 支持 HarmonyOS 开发的 IDE
Flutter SDK 需支持 HarmonyOS/OpenHarmony 平台(包含 flutter-hvigor-plugin
HarmonyOS SDK targetSdkVersion 需与 Flutter 引擎 HAR 兼容(见问题 3
Flutter Module 工程 位于 ../flutter_module(与 flutter_Ohos 同级目录)

3. 最终项目结构

FlutterDemo/
├── flutter_module/                              # Flutter Module 工程
│   ├── lib/main.dart                            # Flutter Dart 入口(路由定义)
│   └── .ohos/
│       ├── local.properties                     # [需配置] flutter.sdk 路径
│       └── flutter_module/                      # Flutter 构建产物桥接模块
│           ├── index.ets                        # 导出 GeneratedPluginRegistrant
│           ├── oh-package.json5                 # 声明 @ohos/flutter_ohos 依赖
│           └── src/main/resources/rawfile/
│               └── flutter_assets/              # Dart 编译产物(自动生成)
│
└── flutter_Ohos/                                # HarmonyOS 原生宿主工程
    ├── local.properties                         # [需配置] flutter.sdk 路径
    ├── include_flutter.ts                       # [新增] Flutter 构建插件配置
    ├── hvigorfile.ts                            # [修改] 加载 Flutter 构建插件
    ├── build-profile.json5                      # [修改] SDK 版本 + 注册 flutter_module
    ├── node_modules/
    │   └── flutter-hvigor-plugin → ...          # [新增] 符号链接到 Flutter SDK
    │
    └── entry/
        ├── oh-package.json5                     # [修改] 添加 @ohos/flutter_module 依赖
        └── src/main/
            ├── module.json5                     # 仅注册 EntryAbility(无需额外 Ability)
            ├── ets/
            │   ├── entryability/
            │   │   └── EntryAbility.ets         # [修改] 注册 FlutterManager
            │   └── pages/
            │       ├── Index.ets                # [修改] 原生首页,按钮跳转 Flutter
            │       └── FlutterIndex.ets         # [新增] FlutterEntry 容器页
            └── resources/base/profile/
                └── main_pages.json              # [修改] 注册 FlutterIndex 页面

4. 集成步骤

步骤 1:配置 Flutter SDK 路径

需要在两个位置配置 flutter.sdk

flutter_Ohos/local.properties

flutter.sdk=/path/to/your/flutter_sdk

flutter_module/.ohos/local.properties(如不存在则创建):

flutter.sdk=/path/to/your/flutter_sdk

替换为实际的 Flutter SDK 路径。两处路径必须一致。

步骤 2:创建 flutter-hvigor-plugin 符号链接

Flutter 的 Hvigor 构建插件位于 Flutter SDK 内部,需通过符号链接让 Node.js 模块解析系统找到它:

cd flutter_Ohos
mkdir -p node_modules
ln -sf /path/to/flutter_sdk/packages/flutter_tools/hvigor node_modules/flutter-hvigor-plugin

如果 flutter 命令已在 PATH 中,可用动态路径:

ln -sf $(dirname $(dirname $(which flutter)))/packages/flutter_tools/hvigor node_modules/flutter-hvigor-plugin

步骤 3:创建 include_flutter.ts

flutter_Ohos/ 根目录创建,用于引入构建插件并指定 Flutter Module 路径:

// include_flutter.ts
import path from 'path'

export { flutterHvigorPlugin, injectNativeModules } from 'flutter-hvigor-plugin'

export function getFlutterProjectPath(): string {
  return path.resolve(__dirname, '../flutter_module')
}

步骤 4:修改根 hvigorfile.ts

加载 Flutter 构建插件:

// hvigorfile.ts
import { appTasks } from '@ohos/hvigor-ohos-plugin';
import { flutterHvigorPlugin, getFlutterProjectPath } from './include_flutter';

export default {
  system: appTasks,
  plugins: [flutterHvigorPlugin(getFlutterProjectPath(), 1)]
}

第二个参数 1 表示 Flutter 模块数量。

步骤 5:修改 build-profile.json5

需要做三件事:调整 SDK 版本关闭 useNormalizedOHMUrl注册 flutter_module

{
  "app": {
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "5.0.5(17)",       // ← 与 Flutter 引擎 HAR 兼容
        "compatibleSdkVersion": "5.0.5(17)",
        "runtimeOS": "HarmonyOS",
        "buildOption": {
          "strictMode": {
            "caseSensitiveCheck": true,
            "useNormalizedOHMUrl": false          // ← 必须关闭
          }
        }
      }
    ]
    // ... signingConfigs, buildModeSet 保持不变
  },
  "modules": [
    {
      "name": "entry",
      "srcPath": "./entry",
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    },
    {
      "name": "flutter_module",
      "srcPath": "../flutter_module/.ohos/flutter_module",    // ← 指向桥接模块
      "targets": [{ "name": "default", "applyToProducts": ["default"] }]
    }
  ]
}

关键点: srcPath 指向 ../flutter_module/.ohos/flutter_module(Flutter 构建产物的桥接模块),而非 ./flutter_module

步骤 6:添加 entry 模块对 flutter_module 的依赖

修改 entry/oh-package.json5

{
  "dependencies": {
    "@ohos/flutter_module": "file:../flutter_module"
  }
}

步骤 7:确保 Flutter 桥接模块的 oh-package.json5 声明了依赖

检查 flutter_module/.ohos/flutter_module/oh-package.json5,确保包含 @ohos/flutter_ohos 依赖:

{
  "name": "@ohos/flutter_module",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "index.ets",
  "author": "",
  "license": "OpenValley",
  "dependencies": {
    "@ohos/flutter_ohos": ""
  }
}

同时确认 flutter_module/.ohos/flutter_module/index.ets 内联了 GeneratedPluginRegistrant

import { FlutterEngine } from '@ohos/flutter_ohos';

export class GeneratedPluginRegistrant {
  static registerWith(flutterEngine: FlutterEngine): void {
  }
}

为什么内联? 原始 index.ets 通过相对路径引用 GeneratedPluginRegistrant,但 ArkTS 编译器禁止跨模块的相对路径导入。内联后可避免此问题。

步骤 8:修改 EntryAbility

EntryAbility 保持继承 UIAbility,但需要向 FlutterManager 注册自身,这样 FlutterEntry 才能通过 getContext() 找到对应的 Ability 和 WindowStage:

// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { FlutterManager } from '@ohos/flutter_ohos';

const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(DOMAIN, 'testTag', 'EntryAbility onCreate');
    FlutterManager.getInstance().pushUIAbility(this);       // ← 注册
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    FlutterManager.getInstance().pushWindowStage(this, windowStage);  // ← 注册
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load content: %{public}s', JSON.stringify(err));
        return;
      }
    });
  }

  onWindowStageDestroy(): void {
    FlutterManager.getInstance().popWindowStage(this);      // ← 注销
  }

  onDestroy(): void {
    FlutterManager.getInstance().popUIAbility(this);        // ← 注销
  }

  onForeground(): void {}
  onBackground(): void {}
}

步骤 9:创建原生首页(Index.ets)

使用 router.pushUrl 跳转到 Flutter 页面,通过 params 传递目标路由:

// entry/src/main/ets/pages/Index.ets
import { router } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  private openFlutterPage(route: string): void {
    router.pushUrl({
      url: 'pages/FlutterIndex',
      params: { 'route': route }
    });
  }

  build() {
    Column() {
      Text('Flutter Module Demo')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 40 })

      Button('Flutter 首页')
        .width('70%').height(45).fontSize(16)
        .margin({ bottom: 12 })
        .onClick(() => this.openFlutterPage('/'))

      Button('计数器页面')
        .width('70%').height(45).fontSize(16)
        .margin({ bottom: 12 })
        .onClick(() => this.openFlutterPage('/counter'))

      Button('个人资料页面')
        .width('70%').height(45).fontSize(16)
        .margin({ bottom: 12 })
        .onClick(() => this.openFlutterPage('/profile'))

      Button('设置页面')
        .width('70%').height(45).fontSize(16)
        .onClick(() => this.openFlutterPage('/settings'))
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }
}

步骤 10:创建 Flutter 容器页(FlutterIndex.ets)

使用 FlutterEntry(非 FlutterAbility)在同一 Ability 内创建 Flutter 引擎并渲染视图:

// entry/src/main/ets/pages/FlutterIndex.ets
import { FlutterEntry, FlutterPage, FlutterEngine, FlutterEngineConfigurator } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '@ohos/flutter_module';
import { router } from '@kit.ArkUI';

class PluginRegistrant implements FlutterEngineConfigurator {
  configureFlutterEngine(flutterEngine: FlutterEngine): void {
    GeneratedPluginRegistrant.registerWith(flutterEngine);
  }

  cleanUpFlutterEngine(flutterEngine: FlutterEngine): void {
  }
}

@Entry
@Component
struct FlutterIndex {
  private flutterEntry: FlutterEntry | null = null;
  @State viewId: string = "";

  aboutToAppear(): void {
    const params = router.getParams() as Record<string, Object>;
    let route = '/';
    if (params !== null && params !== undefined
        && params['route'] !== null && params['route'] !== undefined) {
      route = params['route'] as string;
    }

    this.flutterEntry = new FlutterEntry(getContext(this), { 'route': route });
    this.flutterEntry.setFlutterEngineConfigurator(new PluginRegistrant());
    this.flutterEntry.aboutToAppear();
    this.viewId = this.flutterEntry.getFlutterView().getId();
  }

  aboutToDisappear(): void {
    this.flutterEntry?.aboutToDisappear();
  }

  onPageShow(): void {
    this.flutterEntry?.onPageShow();
  }

  onPageHide(): void {
    this.flutterEntry?.onPageHide();
  }

  onBackPress(): boolean {
    this.flutterEntry?.onBackPress();
    return true;
  }

  build() {
    Column() {
      FlutterPage({ viewId: this.viewId })
    }
  }
}

关键 API 说明:

API 作用
new FlutterEntry(context, params) 创建 FlutterEntry 实例,params 中的 route 对应 Flutter 的 initialRoute
setFlutterEngineConfigurator() 设置引擎配置器,用于注册 Flutter 插件(必须在 aboutToAppear 之前调用)
aboutToAppear() 创建 FlutterEngine 和 FlutterView,初始化引擎
getFlutterView().getId() 获取 viewId,传给 FlutterPage 组件进行渲染
aboutToDisappear() 销毁引擎和视图,释放资源
onPageShow() / onPageHide() 生命周期同步,通知 Flutter 前后台切换
onBackPress() 将返回事件转发给 Flutter 的 NavigationChannel

步骤 11:注册页面

确保 entry/src/main/resources/base/profile/main_pages.json 包含两个页面:

{
  "src": [
    "pages/Index",
    "pages/FlutterIndex"
  ]
}

步骤 12:Flutter Dart 端路由配置

flutter_module/lib/main.dart 中通过 PlatformDispatcher.instance.defaultRouteName 接收原生传递的路由:

import 'dart:ui' as ui;
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Module',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      initialRoute: ui.PlatformDispatcher.instance.defaultRouteName,
      routes: {
        '/': (context) => const MyHomePage(title: 'Flutter Home'),
        '/counter': (context) => const CounterPage(),
        '/profile': (context) => const ProfilePage(),
        '/settings': (context) => const SettingsPage(),
      },
      onUnknownRoute: (settings) {
        return MaterialPageRoute(
          builder: (context) => UnknownRoutePage(routeName: settings.name ?? ''),
        );
      },
    );
  }
}

添加新路由步骤:

  1. main.dartroutes 中添加新的路由映射
  2. Index.ets 中添加对应按钮,调用 this.openFlutterPage('/your_route')
  3. 无需修改 FlutterIndex.ets 或其他配置文件

5. 构建与运行

# 1. 确保 Flutter 依赖已解析
cd ../flutter_module
flutter pub get

# 2. 在 DevEco Studio 中打开 flutter_Ohos 项目
#    执行 File → Sync and Refresh Project

# 3. 点击 Run 编译运行

运行效果

  1. 应用启动 → 显示原生首页(4 个按钮)
  2. 点击「Flutter 首页」→ 显示 Flutter 导航首页(内含 Counter / Profile / Settings 卡片)
  3. 点击「计数器页面」→ 直接打开 Flutter 计数器页面
  4. 点击「个人资料页面」→ 直接打开 Flutter 个人资料页面
  5. 点击「设置页面」→ 直接打开 Flutter 设置页面
  6. 按返回键 → 返回原生首页

6. 常见问题与解决方案

Q1:Cannot find module 'flutter-hvigor-plugin' {#q1}

原因: Flutter 的 Hvigor 构建插件不在 node_modules 中。

解决:

mkdir -p node_modules
ln -sf /path/to/flutter_sdk/packages/flutter_tools/hvigor node_modules/flutter-hvigor-plugin

同时检查 local.propertiesflutter.sdk 路径是否正确。

Q2:Cannot find module '@ohos/flutter_ohos'Cannot find module './src/main/ets/plugins/GeneratedPluginRegistrant' {#q2}

原因: flutter_module/.ohos/flutter_module/index.ets 中通过相对路径引用 GeneratedPluginRegistrant,但 ArkTS 编译器禁止跨模块相对路径导入。

解决:GeneratedPluginRegistrant 类内联到 index.ets 中(见步骤 7),并在 oh-package.json5 中声明 @ohos/flutter_ohos 依赖。

Q3:ArkTS 编译报错 ESObject type is restricted / 大量 deprecated API 警告 {#q3}

原因: 宿主工程的 targetSdkVersion 高于 Flutter 引擎 HAR 的构建版本。Flutter 引擎 HAR 通常针对 SDK 5.x 构建,如果宿主工程使用 6.x SDK,会触发严格模式不兼容。

解决:build-profile.json5 中将 SDK 版本降到兼容范围:

"targetSdkVersion": "5.0.5(17)",
"compatibleSdkVersion": "5.0.5(17)",
"buildOption": {
  "strictMode": {
    "caseSensitiveCheck": true,
    "useNormalizedOHMUrl": false
  }
}

Q4:Failed to resolve OhmUrl for flutter_module/index.ets {#q4}

原因: ArkTS 编译器无法解析 Flutter 桥接模块的路径。

解决:build-profile.json5modules 数组中静态注册桥接模块,srcPath 必须指向 ../flutter_module/.ohos/flutter_module

{
  "name": "flutter_module",
  "srcPath": "../flutter_module/.ohos/flutter_module",
  "targets": [{ "name": "default", "applyToProducts": ["default"] }]
}

Q5:Flutter 页面打不开(白屏或显示错误页面)

可能原因及检查项:

检查项 说明
FlutterManager 是否注册 EntryAbility.onCreate 必须调用 FlutterManager.getInstance().pushUIAbility(this)
WindowStage 是否注册 onWindowStageCreate 必须调用 FlutterManager.getInstance().pushWindowStage(this, windowStage)
FlutterEntry 创建时机 setFlutterEngineConfigurator 必须在 aboutToAppear() 之前调用
Flutter 资产是否构建 检查 flutter_module/.ohos/flutter_module/src/main/resources/rawfile/flutter_assets/ 下是否有 kernel_blob.bin
生命周期是否正确桥接 FlutterIndex.ets 必须实现 aboutToDisappearonPageShowonPageHideonBackPress

Q6:如何在原生和 Flutter 之间传递数据?

通过 Flutter Platform Channel 机制实现:

  • MethodChannel — 方法调用(如触发原生功能)
  • EventChannel — 事件流(如传感器数据)
  • BasicMessageChannel — 通用消息传递

FlutterIndex.ets 中可通过 this.flutterEntry.getFlutterEngine() 获取引擎实例,进而创建 Channel。


7. 关键文件变更清单

文件 操作 说明
local.properties 修改 添加 flutter.sdk 路径
node_modules/flutter-hvigor-plugin 新增 Flutter SDK hvigor 插件的符号链接
include_flutter.ts 新增 Flutter 构建插件配置,指定 Flutter Module 路径
hvigorfile.ts 修改 加载 flutterHvigorPlugin
build-profile.json5 修改 SDK 版本调整 + 注册 flutter_module 桥接模块
entry/oh-package.json5 修改 添加 @ohos/flutter_module 本地依赖
entry/.../EntryAbility.ets 修改 注册/注销 FlutterManager
entry/.../Index.ets 修改 添加按钮,通过 router.pushUrl 跳转到 Flutter
entry/.../FlutterIndex.ets 新增 FlutterEntry 容器页,创建引擎并渲染 Flutter
entry/.../main_pages.json 修改 注册 pages/FlutterIndex
flutter_module/.ohos/local.properties 新增 添加 flutter.sdk 路径
flutter_module/.ohos/flutter_module/index.ets 修改 内联 GeneratedPluginRegistrant
flutter_module/.ohos/flutter_module/oh-package.json5 修改 添加 @ohos/flutter_ohos 依赖声明
flutter_module/lib/main.dart 修改 配置 initialRoute 和路由表

8. FlutterEntry vs FlutterAbility 对比

特性 FlutterEntry(当前方案) FlutterAbility
Ability 数量 1 个(单 UIAbility) 2 个(UIAbility + FlutterAbility)
页面跳转方式 router.pushUrl(页面内导航) startAbility(跨 Ability 导航)
引擎创建位置 在页面 aboutToAppear 中手动创建 在 Ability onCreate 中自动创建
路由传递方式 router.getParams()FlutterEntry 构造参数 Want.parameters['route']
生命周期管理 需手动桥接(onPageShow/onPageHide 等) 自动管理
适用场景 原生为主、Flutter 为辅的混合应用 Flutter 为主的独立页面
返回行为 router.back() 返回原生页 关闭 Ability
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容