鸿蒙如何给 Flutter 项目增加桌面卡片而不破坏主工程结构
适合谁看
-
想给现有 Flutter 项目加鸿蒙桌面卡片的人
-
不想让主工程被原生能力侵入的人
-
想做增量适配的人
问题背景
很多跨端项目接原生入口时,最怕两件事:
-
原生代码侵入主业务层
-
目录结构被打乱
卡片如果接法不当,确实容易出现这两个问题。比如:
-
在 Flutter 主目录里造一套卡片页面
-
卡片数据逻辑和 Flutter 页面逻辑混写
-
配置、生命周期、数据规则揉在一个文件里
项目中的真实场景
食界探味当前卡片相关代码主要集中在鸿蒙壳工程:
app/
├── lib/ ← Flutter 主业务(不为卡片改动)
│ └── core/platform/
│ └── intent_navigation_channel.dart ← 只承接卡片点击后的路由
│
├── ohos/ ← 鸿蒙壳工程(卡片全部在这里)
│ └── entry/src/main/
│ ├── ets/
│ │ ├── formability/ ← 卡片 Ability + 数据
│ │ │ ├── DailyRecommendFormAbility.ets
│ │ │ └── RecommendData.ets
│ │ └── widget/pages/ ← 卡片 UI
│ │ ├── DailyRecommendCard.ets
│ │ ├── SearchCard.ets
│ │ └── WishBoxCard.ets
│ ├── resources/base/profile/ ← 卡片配置
│ │ └── daily_recommend_form_config.json
│ └── module.json5 ← 注册 FormExtensionAbility
Flutter 主业务目录并没有因为卡片而额外长出一套平行结构。
核心实现
一、目录结构——卡片全部收在鸿蒙壳工程
鸿蒙桌面卡片的实现涉及 4 类文件,全部在 app/ohos 下:
|
文件类型 |
位置 |
职责 |
|---|---|---|
|
卡片配置 |
|
定义卡片名称、尺寸、更新频率 |
|
卡片 Ability |
|
管理卡片生命周期 |
|
卡片数据 |
|
提供卡片展示内容 |
|
卡片 UI |
|
渲染卡片界面 |
这 4 类文件全部在鸿蒙壳工程里,Flutter 主工程完全不感知。
关键原则:卡片是鸿蒙系统入口能力,所以实现应该收在鸿蒙壳工程。
二、Flutter 主工程只承接后续行为
真正需要 Flutter 处理的,只有两件事:
-
卡片点击后跳到哪里 — 通过
intent_navigation_channel.dart处理 -
应用内如何继续承接推荐内容 — 通过现有路由和页面处理
// intent_navigation_channel.dart
static const _pageIdToRoute = <String, String>{
'search': '/search',
'ai_assistant': '/ai-assistant',
'wish_box': '/wish-box',
'ingredients': '/ingredients',
'explore': '/explore',
};
Flutter 主工程只需要在路由映射表里加一行,就能承接卡片点击。这属于业务层衔接,不属于卡片实现本体。
三、配置、Ability、数据层分开
食界探味把卡片拆成 3 层:
配置层:daily_recommend_form_config.json
└─ 定义卡片名称、尺寸、更新频率
Ability 层:DailyRecommendFormAbility.ets
└─ 管理卡片生命周期(onAddForm/onUpdateForm/onRemoveForm)
数据层:RecommendData.ets
└─ 提供推荐数据、选择算法、资源校验
这 3 层各司其职,互不干扰。改配置不需要动 Ability,改数据不需要动 Ability,改 Ability 不需要动配置。
四、接入步骤——增量式,不破坏现有结构
给 Flutter 项目加鸿蒙桌面卡片的步骤:
步骤 1:在鸿蒙壳工程创建目录
app/ohos/entry/src/main/
├── ets/formability/ ← 新建
├── ets/widget/pages/ ← 新建
└── resources/base/profile/ ← 已有
步骤 2:添加卡片配置文件
// daily_recommend_form_config.json
{
"forms": [{
"name": "DailyRecommendCard",
"displayName": "今日探味",
"src": "./ets/widget/pages/DailyRecommendCard.ets",
"updateEnabled": true,
"scheduledUpdateTime": "00:05",
"defaultDimension": "2*4"
}]
}
步骤 3:在 module.json5 注册
{
"extensionAbilities": [
{
"name": "DailyRecommendFormAbility",
"srcEntry": "./ets/formability/DailyRecommendFormAbility.ets",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:daily_recommend_form_config"
}
]
}
]
}
步骤 4:创建 Ability 和数据层
// DailyRecommendFormAbility.ets
import { getRecommendOfToday, resolveImageResName } from './RecommendData';
export default class DailyRecommendFormAbility extends FormExtensionAbility {
onAddForm(want: Want): formBindingData.FormBindingData {
const item = getRecommendOfToday();
return formBindingData.createFormBindingData({
dishName: item.name,
dishImage: resolveImageResName(item.imageResName),
});
}
}
步骤 5:创建卡片 UI
// DailyRecommendCard.ets
@Component
struct DailyRecommendCard {
@LocalStorageProp('dishName') dishName: string = '环球美食';
build() {
Row() {
Text(this.dishName).fontSize(18)
}
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: { pageId: 'dish_detail', dishId: this.dishId }
});
})
}
}
步骤 6:Flutter 侧加路由映射(可选)
// intent_navigation_channel.dart
static const _pageIdToRoute = <String, String>{
'search': '/search',
// ... 已有路由
};
6 步完成后,鸿蒙桌面卡片就接入了。Flutter 主工程几乎没改动。
五、为什么这种接入方式不破坏主工程
|
担心 |
实际情况 |
|---|---|
|
卡片代码会侵入 Flutter 主目录 |
❌ 卡片全部在 |
|
Flutter 主工程需要新增页面 |
❌ 只需在路由映射表加一行 |
|
卡片数据逻辑会影响 Flutter 页面 |
❌ 数据层在鸿蒙壳工程 |
|
卡片配置会和 Flutter 配置冲突 |
❌ 配置文件完全独立 |
|
卡片 UI 会影响 Flutter 渲染 |
❌ 卡片 UI 是 ArkTS,不是 Flutter |
六、和 Intents Kit 的接入方式对比
鸿蒙桌面卡片和 Intents Kit 的接入方式非常相似:
|
维度 |
鸿蒙桌面卡片 |
Intents Kit |
|---|---|---|
|
实现位置 |
|
|
|
Flutter 改动 |
路由映射表加一行 |
路由映射表加一行 |
|
配置文件 |
|
|
|
Ability |
|
|
|
卡片 UI |
ArkTS 声明式 UI |
无(系统搜索展示) |
两者都遵循"壳工程增量接入"的原则:平台能力收在鸿蒙壳工程,Flutter 只承接后续路由。
关键代码位置
|
文件 |
作用 |
|---|---|
|
|
鸿蒙卡片 Ability |
|
|
卡片数据层 |
|
|
卡片 UI |
|
|
卡片配置 |
|
|
注册 FormExtensionAbility |
|
|
Flutter 路由承接 |
目录结构对比——接入前 vs 接入后
接入前:
app/
├── lib/ ← Flutter 主业务
├── ohos/ ← 鸿蒙壳工程(基础)
└── android/ ← Android 壳工程
接入后:
app/
├── lib/ ← Flutter 主业务(几乎不变)
│ └── core/platform/
│ └── intent_navigation_channel.dart ← 加一行路由映射
│
├── ohos/ ← 鸿蒙壳工程(新增卡片)
│ └── entry/src/main/
│ ├── ets/
│ │ ├── formability/ ← 新增
│ │ └── widget/pages/ ← 新增
│ ├── resources/base/profile/ ← 新增配置
│ └── module.json5 ← 加注册
│
└── android/ ← Android 壳工程(不变)
Flutter 主业务目录完全不变,只有鸿蒙壳工程有增量。
常见坑
-
在 Flutter 主目录里额外造一套卡片页面结构 — 卡片应该全部在
app/ohos下 -
卡片数据逻辑和 Flutter 页面逻辑混写 — 数据层在鸿蒙壳工程,Flutter 只承接路由
-
配置、生命周期、数据规则揉在一个文件里 — 应该拆成配置/Ability/数据三层
-
卡片点击后没有稳定应用内承接 — Flutter 侧需要在路由映射表里加一行
-
没有在 module.json5 注册 — 鸿蒙系统不会发现这张卡片
-
卡片 UI 和 Flutter UI 混用 — 卡片 UI 是 ArkTS,不是 Flutter Widget
可复用模板
鸿蒙卡片接入步骤模板
步骤 1:在 app/ohos/entry/src/main/ 下创建目录
├── ets/formability/ ← Ability + 数据
├── ets/widget/pages/ ← 卡片 UI
└── resources/base/profile/ ← 配置文件
步骤 2:添加配置文件(daily_recommend_form_config.json)
步骤 3:在 module.json5 注册 FormExtensionAbility
步骤 4:创建 Ability(DailyRecommendFormAbility.ets)
步骤 5:创建数据层(RecommendData.ets)
步骤 6:创建卡片 UI(DailyRecommendCard.ets)
步骤 7:Flutter 侧加路由映射(intent_navigation_channel.dart)
鸿蒙壳工程目录模板
app/ohos/entry/src/main/
├── ets/
│ ├── formability/
│ │ ├── YourFormAbility.ets ← 卡片 Ability
│ │ └── YourData.ets ← 卡片数据
│ └── widget/pages/
│ └── YourCard.ets ← 卡片 UI
├── resources/base/profile/
│ └── your_form_config.json ← 卡片配置
└── module.json5 ← 注册
module.json5 注册模板
{
"extensionAbilities": [
{
"name": "YourFormAbility",
"srcEntry": "./ets/formability/YourFormAbility.ets",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:your_form_config"
}
]
}
]
}
本篇总结
给 Flutter 项目加鸿蒙桌面卡片,不等于重构主业务工程。食界探味的做法展示了"壳工程增量接入"的思路:
-
卡片全部在鸿蒙壳工程 —
app/ohos下的 formability、widget、profile -
Flutter 主工程几乎不变 — 只需在路由映射表加一行
-
配置/Ability/数据三层分离 — 各司其职,互不干扰
-
和 Intents Kit 接入方式一致 — 都遵循"壳工程增量接入"原则
只要分清"卡片实现"和"卡片承接",主工程结构就不会乱。这种增量式接入是鸿蒙 + Flutter 项目最稳的做法。
更多推荐




所有评论(0)