feat: 更新鸿蒙应用配置与功能优化

- 添加鸿蒙分层图标配置和生成脚本
- 修复数据导出JSON解析问题
- 优化关于页面和团队信息展示
- 更新应用版本至1.4.1
- 清理代码警告和冗余文件
- 添加字体和二维码测试脚本
- 完善鸿蒙适配文档和指南
This commit is contained in:
Developer
2026-04-25 09:52:06 +08:00
parent 3c90407bb5
commit 4ec348b28e
63 changed files with 4538 additions and 1818 deletions

View File

@@ -0,0 +1,239 @@
# Flutter 包鸿蒙适配指南
> 文档创建: 2026-04-25 | 最后更新: 2026-04-25
> 适用对象: 所有需要在 HarmonyOS 上使用 Flutter 第三方包的开发者
> 核心原则: **纯Dart包零适配原生插件需ets实现**
---
## 一、核心结论(先看这个)
| 包类型 | 需要做什么 | ohos目录 |
|--------|-----------|---------|
| 🟢 **纯 Dart 包** | 仅改版本号后缀 `-ohos.1` | ❌ **禁止创建!** |
| 🔴 **原生插件** | 写 ets 原生代码 + DevEco Studio 打 har | ✅ 必须有 |
> ⛔ **最重要的一条规则**: 纯 Dart 包绝对不能创建 `ohos/` 目录!
> Flutter 引擎启动时会扫描所有 ohos 模块并 copyResource
> 空壳模块引用不存在的 `libs/flutter.har` → 直接崩溃(错误码 9001005
---
## 二、如何判断包类型
### 2.1 快速判断
```
打开包根目录,检查以下三项:
├── 是否有 android/ 或 ios/ 目录?
│ ├── 无 → 🟢 纯Dart包 → 跳到第三章
│ └── 有 → 继续判断
├── pubspec.yaml 中是否有 flutter.plugin 配置?
│ ├── 无 → 🟢 纯Dart包有平台目录但无插件声明→ 跳到第三章
│ └── 有 → 🔴 原生插件 → 跳到第四章
└── 代码中是否使用了 MethodChannel / EventChannel / FFI
├── 无 → 🟢 纯Dart包 → 跳到第三章
└── 有 → 🔴 原生插件 → 跳到第四章
```
### 2.2 判断对照表
| 检查项 | 🟢 纯 Dart 包 | 🔴 原生插件 |
|--------|-------------|-----------|
| `android/` 目录 | ❌ 无 | ✅ 有 |
| `ios/` 目录 | ❌ 无 | ✅ 有 |
| `flutter.plugin` 声明 | ❌ 无 | ✅ 有 |
| MethodChannel 使用 | ❌ 无 | ✅ 有 |
| EventChannel 使用 | ❌ 无 | ✅ 有 |
| FFI (dart:ffi) 使用 | ❌ 无 | ✅ 有 |
| 典型例子 | fl_chart, badges, qr, mailer | mobile_scanner, file_picker, camera |
### 2.3 常见纯 Dart 包类别
这些类型的包 **100% 是纯 Dart**,无需任何原生适配:
| 类别 | 代表包 | 说明 |
|------|--------|------|
| UI 组件 | fl_chart, badges, card_swiper | 纯 Widget 渲染 |
| 布局组件 | staggered_grid_view | 纯布局算法 |
| 图片处理 | cached_network_image | 网络+缓存(条件导入) |
| 文本渲染 | flutter_markdown | Markdown 解析+渲染 |
| 编码工具 | qr (二维码生成) | 纯算法实现 |
| 网络协议 | mailer (SMTP) | Socket 通信 |
| 文档生成 | docs_gee (DOCX/PDF) | 字节流组装 |
---
## 三、纯 Dart 包适配步骤3步搞定
### 步骤 1: 拉取源码
```bash
cd packages
git clone --depth 1 --branch <版本号> <github地址> <包名>
```
### 步骤 2: 修改版本号
编辑 `pubspec.yaml`:
```yaml
# 修改前
version: 1.2.0
# 修改后(添加 -ohos.1 后缀标识已适配)
version: 1.2.0-ohos.1
```
### 步骤 3: 项目引用
编辑项目根目录的 `pubspec.yaml`:
```yaml
dependencies:
<包名>:
path: packages/<包名>
```
然后执行:
```bash
flutter pub get && flutter analyze --no-pub
```
### ✅ 完成!
> 就这么简单。不需要创建任何 ohos 文件,不需要写 Plugin.ets不需要配置 module.json5。
> Flutter 的 AOT 编译器会将纯 Dart 代码直接编译为各平台的机器码。
---
## 四、原生插件适配流程
> 详细步骤请参考 [ohos平台适配flutter三方库指导.md](./ohos平台适配flutter三方库指导.md)
### 4.1 概览流程
```
1. flutter create --platforms ohos <plugin>_ohos # 创建ohos模块骨架
2. 复制 android 版 lib/dart 代码,将 android 改为 ohos
3. DevEco Studio 编写 ets 原生代码(参考 android/ios 实现)
4. pubspec.yaml 添加 flutter.plugin.platforms.ohos 声明
5. DevEco Studio → Build → Make Module 打 .har 包
6. 确保 libs/flutter.har 存在(从其他已适配插件复制)
7. flutter create --platforms ohos example 创建验证工程
8. flutter run -d <device> 验证功能正常
```
### 4.2 关键文件清单
```
<plugin>/ohos/
├── Index.ets # 插件入口export Plugin 类
├── oh-package.json5 # 依赖声明,必须引用 libs/flutter.har
├── build-profile.json5 # 构建配置
├── libs/
│ └── flutter.har # ⚠️ 必须存在Flutter引擎库
└── src/main/
├── module.json5 # 模块声明type: "har"
├── resources/base/profile/ # 资源配置
└── ets/components/plugin/
└── <PluginName>Plugin.ets # 原生实现MethodChannel Handler
```
### 4.3 oh-package.json5 必要配置
```json
{
"name": "<plugin_name>",
"version": "1.0.0",
"description": "HarmonyOS implementation of <plugin>",
"main": "Index.ets",
"dependencies": {
"@ohos/flutter_ohos": "file:libs/flutter.har"
}
}
```
> ⚠️ `libs/flutter.har` 必须真实存在!否则编译通过但运行时闪退(9001005)。
---
## 五、⛔ 踩坑记录(血泪教训)
### 5.1 最严重: 给纯Dart包创建了空壳 ohos 目录
**时间**: 2026-04-25
**错误**: 为 9 个纯 Dart 包创建了空壳 ohos 目录(含 Index.ets、空壳 Plugin.ets 等)
**现象**: 鸿蒙端启动即闪退,无法进入任何页面
**错误码**: `9001005` (Invalid relative path)
**堆栈**: `copyResource → startInitialization → checkLoader → setupFlutterEngine → onCreate`
**根因分析**:
```
Flutter 引擎启动流程:
1. 扫描项目中所有含 ohos/ 目录的模块
2. 读取每个模块的 oh-package.json5
3. 解析 dependencies 中的 @ohos/flutter_ohos: "file:libs/flutter.har"
4. 尝试 copyResource 将 flutter.har 复制到应用沙箱
5. libs/flutter.har 不存在 → 9001005 崩溃
```
**修复**: 删除全部 9 个纯 Dart 包的 ohos 目录
**预防**:
```
在创建 ohos 目录之前,先确认:
✅ 该包是否真的有原生代码MethodChannel/FFI
✅ 该包的 android/ios 目录下是否有真实的 Kotlin/Swift 实现?
如果都是 NO → 禁止创建 ohos 目录!
```
### 5.2 其他常见问题
| 问题 | 原因 | 解决方案 |
|------|------|---------|
| 编译通过但运行闪退 | ohos目录存在但 libs/flutter.har 缺失 | 从已适配插件复制 har 文件 |
| analyze 报错找不到类 | pubspec.yaml 中 pluginClass 配置错误 | 检查 dart 侧注册的类名 |
| MethodChannel 无响应 | ets 侧 handler 未正确注册 | 检查 onAttachedToEngine 中的 setMethodCallHandler |
| har 包体积过大 | example/ohos 未删除 | 删除 example 下的 ohos 目录 |
---
## 六、检查清单(适配完成后逐项确认)
### 纯 Dart 包
- [ ] 已修改 version 后缀为 `-ohos.1`
- [ ] **没有** ohos/ 目录
- [ ] **没有** Index.ets / Plugin.ets / module.json5
- [ ] `flutter pub get` 通过
- [ ] `flutter analyze --no-pub` 通过
### 原生插件
- [ ] 已修改 version 后缀为 `-ohos.1`
- [ ] ohos/ 目录结构完整
- [ ] `libs/flutter.har` 文件真实存在(非空文件)
- [ ] `oh-package.json5` 正确引用 flutter.har
- [ ] `module.json5` 中 type 为 `"har"`
- [ ] Index.ets 正确 export Plugin 类
- [ ] ets 代码实现了 MethodChannel handler
- [ ] pubspec.yaml 中声明了 `flutter.plugin.platforms.ohos`
- [ ] DevEco Studio 编译 har 成功
- [ ] example 工程可正常运行
---
## 七、参考文档
| 文档 | 用途 |
|------|------|
| [ohos平台适配flutter三方库指导.md](./ohos平台适配flutter三方库指导.md) | 原生插件完整适配流程(含截图和详细步骤) |
| [开发FFI plugin.md](./开发FFI plugin.md) | FFI 插件开发指南C/C++ 原生库) |
| [OpenHarmony应用如何集成Flutter模块.md](./OpenHarmony应用如何集成Flutter模块.md) | Flutter 模块集成到鸿蒙应用的架构文档 |
| [FlutterChannel通信.md](./如何使用Flutter与OpenHarmony通信%20FlutterChannel.md) | MethodChannel / EventChannel / BasicMessageChannel API 用法 |
| [本地已适配鸿蒙的库.md](./本地已适配鸿蒙的库.md) | 本项目已适配的具体包清单和使用示例 |

View File

@@ -2,8 +2,8 @@
* Use these variables when you tailor your ArkTS code. They must be of the const type.
*/
export const HAR_VERSION = '1.0.0';
export const BUILD_MODE_NAME = 'debug';
export const DEBUG = true;
export const BUILD_MODE_NAME = 'release';
export const DEBUG = false;
export const TARGET_NAME = 'default';
/**

View File

@@ -2,8 +2,8 @@
"app": {
"bundleName": "cute.major.kitchen",
"debug": true,
"versionCode": 26042402,
"versionName": "1.4.0",
"versionCode": 26042503,
"versionName": "1.4.1",
"minAPIVersion": 50005017,
"targetAPIVersion": 60100023,
"apiReleaseType": "Release",

View File

@@ -2,8 +2,8 @@
"app": {
"bundleName": "cute.major.kitchen",
"debug": false,
"versionCode": 26042402,
"versionName": "1.4.0",
"versionCode": 26042503,
"versionName": "1.4.1",
"minAPIVersion": 50001013,
"targetAPIVersion": 60100023,
"apiReleaseType": "Release",

View File

@@ -6,20 +6,27 @@
"lockfileVersion": 3,
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
"specifiers": {
"@ohos/flutter_ohos@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/flutter_embedding_debug.har": "@ohos/flutter_ohos@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/flutter_embedding_debug.har",
"flutter_native_arm64_v8a@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/arm64_v8a_debug.har": "flutter_native_arm64_v8a@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/arm64_v8a_debug.har"
"@ohos/flutter_ohos@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/flutter_embedding_release.har": "@ohos/flutter_ohos@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/flutter_embedding_release.har",
"flutter_native_arm64_v8a@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/arm64_v8a_release.har": "flutter_native_arm64_v8a@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/arm64_v8a_release.har",
"flutter_native_x86_64@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-x64-release/x86_64_release.har": "flutter_native_x86_64@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-x64-release/x86_64_release.har"
},
"packages": {
"@ohos/flutter_ohos@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/flutter_embedding_debug.har": {
"@ohos/flutter_ohos@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/flutter_embedding_release.har": {
"name": "@ohos/flutter_ohos",
"version": "1.0.0-e34a685f4b",
"resolved": "../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/flutter_embedding_debug.har",
"resolved": "../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/flutter_embedding_release.har",
"registryType": "local"
},
"flutter_native_arm64_v8a@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/arm64_v8a_debug.har": {
"flutter_native_arm64_v8a@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/arm64_v8a_release.har": {
"name": "flutter_native_arm64_v8a",
"version": "1.0.0-e34a685f4b",
"resolved": "../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64/arm64_v8a_debug.har",
"resolved": "../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-arm64-release/arm64_v8a_release.har",
"registryType": "local"
},
"flutter_native_x86_64@../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-x64-release/x86_64_release.har": {
"name": "flutter_native_x86_64",
"version": "1.0.0-e34a685f4b",
"resolved": "../../../../../../../sdk/flutter-ohos/flutter_flutter/bin/cache/artifacts/engine/ohos-x64-release/x86_64_release.har",
"registryType": "local"
}
}

View File

@@ -2,8 +2,8 @@
* Use these variables when you tailor your ArkTS code. They must be of the const type.
*/
export const HAR_VERSION = '1.0.0-e34a685f4b';
export const BUILD_MODE_NAME = 'debug';
export const DEBUG = true;
export const BUILD_MODE_NAME = 'release';
export const DEBUG = false;
export const TARGET_NAME = 'default';
/**

View File

@@ -0,0 +1,3 @@
-keep-property-name
flutter
native*

View File

@@ -1 +1 @@
{"license":"Apache-2.0","author":"","name":"@ohos/flutter_ohos","description":"The embedder of flutter in ohos.","main":"index.ets","version":"1.0.0-e34a685f4b","dependencies":{},"devDependencies":{"@types/libflutter.so":"file:./src/main/cpp/types/libflutter"},"metadata":{"workers":["./src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets"],"sourceRoots":["./src/main"],"debug":true},"compatibleSdkVersionStage":"beta1","compatibleSdkVersion":12,"compatibleSdkType":"HarmonyOS","obfuscated":false}
{"license":"Apache-2.0","author":"","name":"@ohos/flutter_ohos","description":"The embedder of flutter in ohos.","main":"index.ets","version":"1.0.0-e34a685f4b","dependencies":{},"devDependencies":{"@types/libflutter.so":"file:./src/main/cpp/types/libflutter"},"metadata":{"workers":["./src/main/ets/embedding/engine/workers/PlatformChannelWorker.ets"],"sourceRoots":["./src/main"],"debug":false},"compatibleSdkVersionStage":"beta1","compatibleSdkVersion":12,"compatibleSdkType":"HarmonyOS","obfuscated":false}

View File

@@ -1,7 +1,7 @@
{
"app": {
"bundleName": "com.example.config",
"debug": true,
"debug": false,
"versionCode": 1000000,
"versionName": "1.0.0",
"minAPIVersion": 50000012,
@@ -13,7 +13,7 @@
"compileSdkType": "HarmonyOS",
"appEnvironments": [],
"bundleType": "app",
"buildMode": "debug"
"buildMode": "release"
},
"module": {
"name": "flutter",

View File

@@ -2,8 +2,8 @@
* Use these variables when you tailor your ArkTS code. They must be of the const type.
*/
export const HAR_VERSION = '1.0.0-e34a685f4b';
export const BUILD_MODE_NAME = 'debug';
export const DEBUG = true;
export const BUILD_MODE_NAME = 'release';
export const DEBUG = false;
export const TARGET_NAME = 'default';
/**

View File

@@ -1 +1 @@
{"name":"flutter_native_arm64_v8a","version":"1.0.0-e34a685f4b","description":"Place so files for flutter on ohos.","main":"Index.ets","author":"","license":"Apache-2.0","dependencies":{},"metadata":{"sourceRoots":["./src/main"],"debug":true,"nativeDebugSymbol":false},"compatibleSdkVersionStage":"beta1","compatibleSdkVersion":12,"compatibleSdkType":"HarmonyOS","obfuscated":false}
{"name":"flutter_native_arm64_v8a","version":"1.0.0-e34a685f4b","description":"Place so files for flutter on ohos.","main":"Index.ets","author":"","license":"Apache-2.0","dependencies":{},"metadata":{"sourceRoots":["./src/main"],"debug":false,"nativeDebugSymbol":false},"compatibleSdkVersionStage":"beta1","compatibleSdkVersion":12,"compatibleSdkType":"HarmonyOS","obfuscated":false}

View File

@@ -1,7 +1,7 @@
{
"app": {
"bundleName": "com.example.config",
"debug": true,
"debug": false,
"versionCode": 1000000,
"versionName": "1.0.0",
"minAPIVersion": 50000012,
@@ -13,7 +13,7 @@
"compileSdkType": "HarmonyOS",
"appEnvironments": [],
"bundleType": "app",
"buildMode": "debug"
"buildMode": "release"
},
"module": {
"name": "flutter_native",

View File

@@ -0,0 +1,17 @@
/**
* Use these variables when you tailor your ArkTS code. They must be of the const type.
*/
export const HAR_VERSION = '1.0.0-e34a685f4b';
export const BUILD_MODE_NAME = 'release';
export const DEBUG = false;
export const TARGET_NAME = 'default';
/**
* BuildProfile Class is used only for compatibility purposes.
*/
export default class BuildProfile {
static readonly HAR_VERSION = HAR_VERSION;
static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
static readonly DEBUG = DEBUG;
static readonly TARGET_NAME = TARGET_NAME;
}

View File

@@ -0,0 +1,34 @@
{
"apiType": "stageMode",
"buildOption": {
"nativeLib": {
"debugSymbol": {
"strip": false,
"exclude": []
}
}
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": false,
"files": [
"./obfuscation-rules.txt"
]
},
"consumerFiles": [
"./consumer-rules.txt"
]
}
},
},
],
"targets": [
{
"name": "default"
}
]
}

View File

@@ -0,0 +1,6 @@
import { harTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}

View File

@@ -0,0 +1,23 @@
# Define project specific obfuscation rules here.
# You can include the obfuscation configuration files in the current module's build-profile.json5.
#
# For more details, see
# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
# Obfuscation options:
# -disable-obfuscation: disable all obfuscations
# -enable-property-obfuscation: obfuscate the property names
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
# -compact: remove unnecessary blank spaces and all line feeds
# -remove-log: remove all console.* statements
# -print-namecache: print the name cache that contains the mapping from the old names to new names
# -apply-namecache: reuse the given cache file
# Keep options:
# -keep-property-name: specifies property names that you want to keep
# -keep-global-name: specifies names that you want to keep in the global scope
-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
-enable-export-obfuscation

View File

@@ -0,0 +1 @@
{"name":"flutter_native_x86_64","version":"1.0.0-e34a685f4b","description":"Place so files for flutter on ohos.","main":"Index.ets","author":"","license":"Apache-2.0","dependencies":{},"metadata":{"sourceRoots":["./src/main"],"debug":false,"nativeDebugSymbol":false},"compatibleSdkVersionStage":"beta1","compatibleSdkVersion":12,"compatibleSdkType":"HarmonyOS","obfuscated":false}

View File

@@ -0,0 +1,30 @@
{
"app": {
"bundleName": "com.example.config",
"debug": false,
"versionCode": 1000000,
"versionName": "1.0.0",
"minAPIVersion": 50000012,
"targetAPIVersion": 60001021,
"apiReleaseType": "Release",
"targetMinorAPIVersion": 0,
"targetPatchAPIVersion": 0,
"compileSdkVersion": "6.0.1.112",
"compileSdkType": "HarmonyOS",
"appEnvironments": [],
"bundleType": "app",
"buildMode": "release"
},
"module": {
"name": "flutter_native",
"type": "har",
"deviceTypes": [
"default"
],
"packageName": "flutter_native_x86_64",
"installationFree": false,
"virtualMachine": "ark",
"compileMode": "esmodule",
"dependencies": []
}
}

View File

@@ -1,7 +1,8 @@
# 鸿蒙适配方案
> 文档创建: 2026-04-09 | 最后更新: 2026-04-22
> 文档创建: 2026-04-09 | 最后更新: 2026-04-25
> 适配策略: 纯 Dart 包零成本适配 + 原生插件完整适配
> ⚠️ 重要教训: 纯Dart包禁止添加ohos目录会导致启动闪退(9001005)
---
@@ -12,22 +13,35 @@
| `android/` `ios/` 目录 | ❌ 无 | ✅ 有 |
| `flutter.plugin` 声明 | ❌ 无 | ✅ 有 |
| MethodChannel / FFI | ❌ 无 | ✅ 有 |
| 适配方式 | 空壳 ohos 目录 | ets 原生实现 + har 包 |
| 工作量 | 5min | 1-3 天 |
| 适配方式 | **仅改版本号,无ohos目录** | ets 原生实现 + har 包 |
| 工作量 | 1min | 1-3 天 |
### 快速判断流程
```
新包是否包含原生代码?
├── 否(纯 Dart→ ✅ 零适配:改版本号 + 空壳 ohos 目录
├── 否(纯 Dart→ ✅ 零适配:改版本号即可!
│ ⛔ 禁止创建 ohos 目录!会导致 Invalid relative path (9001005) 启动闪退
│ Flutter引擎启动时会扫描所有 ohos 模块并 copyResource
│ 空壳模块引用不存在的 libs/flutter.har → 直接崩溃
└── 是(有原生代码)
├── MethodChannel → 需 ets 实现 + DevEco Studio 打 har
└── FFI → 需编译鸿蒙版 .so + CMakeLists
```
### ⛔ 血泪教训 (2026-04-25)
2026-04-25 因给9个纯Dart包创建了空壳ohos目录导致鸿蒙端持续闪退
- 错误码 `9001005` (Invalid relative path)
- 堆栈 `copyResource → startInitialization → onCreate`
- 根因:空壳 `oh-package.json5` 引用不存在的 `libs/flutter.har`
- **修复**: 删除全部9个纯Dart包的 ohos 目录
- **结论**: 纯Dart包 = 不需要任何ohos文件
---
## 二、纯 Dart 包适配步骤(通用模板
## 二、纯 Dart 包适配步骤(正确做法
### 2.1 拉取源码
@@ -40,66 +54,7 @@ git clone --depth 1 --branch <version> <github-url> <package-name>
`pubspec.yaml``version: x.x.x``x.x.x-ohos.1`
### 2.3 创建空壳 ohos 目录
```
packages/<name>/ohos/
├── Index.ets
├── oh-package.json5
├── build-profile.json5
├── libs/ # flutter.har 占位(空目录)
└── src/main/
├── module.json5
├── resources/base/profile/ # 空目录
└── ets/components/plugin/
└── <Name>Plugin.ets # 空壳类
```
#### 通用文件模板(所有纯 Dart 包共用)
**`Index.ets`**
```typescript
import <Name>Plugin from './src/main/ets/components/plugin/<Name>Plugin';
export default <Name>Plugin;
```
**`oh-package.json5`**
```json
{
"name": "<package_name>",
"version": "<version>-ohos.1",
"description": "<desc> - HarmonyOS adaptation",
"main": "Index.ets",
"author": "",
"license": "<license>",
"dependencies": {
"@ohos/flutter_ohos": "file:libs/flutter.har"
}
}
```
**`build-profile.json5`**
```json
{ "apiType": "stageMode", "buildOption": {}, "targets": [{ "name": "default" }] }
```
**`module.json5`**
```json
{
"module": {
"name": "<package_name>",
"type": "har",
"deviceTypes": ["default", "tablet"]
}
}
```
**`<Name>Plugin.ets`**
```typescript
export default class <Name>Plugin {}
```
### 2.4 项目引用 & 验证
### 2.3 项目引用 & 验证
```yaml
# pubspec.yaml
@@ -112,6 +67,11 @@ dependencies:
flutter pub get && flutter analyze --no-pub
```
### ✅ 完成!
> **就这么简单!** 纯Dart代码跨平台编译不需要任何原生适配。
> 不要创建 ohos/ 目录、不要写 Plugin.ets、不要配置 module.json5。
---
## 三、原生插件适配流程(参考)
@@ -142,28 +102,36 @@ flutter pub get && flutter analyze --no-pub
---
## 五、已适配纯 Dart 包清单
## 五、已适配包清单
### 5.1 总览表
| # | 包名 | 原版本 | 适配版本 | 日期 | 状态 |
|---|------|--------|---------|------|------|
| 1 | fl_chart | 1.2.0 | 1.2.0-ohos.1 | 2026-04-09 | ✅ Android / HarmonyOS |
| 2 | badges | 3.2.0 | 3.2.0-ohos.1 | 2026-04-10 | ✅ analyze 通过 |
| 3 | flutter_staggered_grid_view | 0.7.0 | 0.7.0-ohos.1 | 2026-04-12 | ✅ analyze 通过 |
| 4 | cached_network_image | 3.4.1 | 3.4.1-ohos.1 | 2026-04-12 | ✅ analyze 通过 |
| 5 | flutter_markdown_plus | 1.0.7 | 1.0.7-ohos.1 | 2026-04-14 | ✅ analyze 通过 |
| 6 | flutter_card_swiper | 7.2.0 | 7.2.0-ohos.1 | 2026-04-14 | ✅ analyze 通过 |
| 7 | qr | 3.0.2 | 3.0.2-ohos.1 | 2026-04-19 | ✅ pub get 通过 |
| 8 | mailer | 7.1.0 | 7.1.0-ohos.1 | 2026-04-19 | ✅ pub get 通过 |
| 9 | mobile_scanner | 7.2.0 | 7.2.0+ohos | 2026-04-22 | ✅ 原生插件含鸿蒙ets实现 |
| 10 | docs_gee | 1.3.2 | 1.3.2-ohos.1 | 2026-04-24 | ✅ 纯DartDOCX/PDF生成库 |
| # | 包名 | 类型 | 原版本 | 适配版本 | ohos目录 | 日期 |
|---|------|------|--------|---------|---------|------|
| 1 | fl_chart | 🟢 纯Dart | 1.2.0 | 1.2.0-ohos.1 | ❌ 无需 | 2026-04-09 |
| 2 | badges | 🟢 纯Dart | 3.2.0 | 3.2.0-ohos.1 | ❌ 无需 | 2026-04-10 |
| 3 | flutter_staggered_grid_view | 🟢 纯Dart | 0.7.0 | 0.7.0-ohos.1 | ❌ 无需 | 2026-04-12 |
| 4 | cached_network_image | 🟢 纯Dart | 3.4.1 | 3.4.1-ohos.1 | ❌ 无需 | 2026-04-12 |
| 5 | flutter_markdown_plus | 🟢 纯Dart | 1.0.7 | 1.0.7-ohos.1 | ❌ 无需 | 2026-04-14 |
| 6 | flutter_card_swiper | 🟢 纯Dart | 7.2.0 | 7.2.0-ohos.1 | ❌ 无需 | 2026-04-14 |
| 7 | qr | 🟢 纯Dart | 3.0.2 | 3.0.2-ohos.1 | ❌ 无需 | 2026-04-19 |
| 8 | mailer | 🟢 纯Dart | 7.1.0 | 7.1.0-ohos.1 | ❌ 无需 | 2026-04-19 |
| 9 | docs_gee | 🟢 纯Dart | 1.3.2 | 1.3.2-ohos.1 | ❌ 无需 | 2026-04-24 |
| 10 | **pdf** | 🟢 纯Dart | 3.12.0 | 3.12.0-ohos.1 | ❌ 无需 | 2026-04-25 |
| 11 | mobile_scanner | 🔴 原生插件 | 7.2.0 | 7.2.0+ohos | ✅ 有ets实现 | 2026-04-22 |
| 12 | file_picker | 🔴 原生插件 | - | 1.0.1 | ✅ 有ets实现 | 已适配 |
| 13 | fluttertoast_ohos | 🔴 原生插件 | - | 1.0.0 | ✅ 有ets实现 | 已适配 |
> **🟢 纯Dart (10个)**: 仅改版本号无任何ohos文件。Flutter AOT编译直接运行。
> **🔴 原生插件 (3个)**: 含ets原生代码 + flutter.har依赖需要DevEco Studio编译。
### 5.2 各包克隆命令速查
```bash
cd packages
# ====== 🟢 纯Dart包仅改版本号即可======
# 1. fl_chart
git clone --depth 1 --branch 1.2.0 https://github.com/imaNNeo/fl_chart.git fl_chart
@@ -189,13 +157,19 @@ git clone --depth 1 --branch v3.0.2 https://github.com/kevmoo/qr.dart.git qr
# 8. mailer
git clone --depth 1 --branch v7.1.0 https://github.com/dart-mailer/mailer.git mailer
# 9. mobile_scanner官方v7.2.0 + 鸿蒙适配合并
git clone --depth 1 --branch v7.2.0 https://github.com/juliansteenbakker/mobile_scanner.git mobile_scanner
# 合并鸿蒙适配ohos/ 目录、ohos_surface_producer_delegate.dart、surface_producer_delegate.dart、rotated_preview.dart 鸿蒙旋转修复
# 10. docs_gee纯DartDOCX/PDF文档生成库
# 9. docs_gee纯DartDOCX/PDF文档生成库
git clone --depth 1 https://github.com/erykkruk/docs_gee.git docs_gee
# 实际代码在 docx_generator/ 子目录,引用路径: packages/docs_gee/docx_generator
# 10. pdf纯Dart专业PDF生成库GitHub 2k+ stars
git clone --depth 1 https://github.com/DavBfr/dart_pdf.git pdf
# monorepo结构实际代码在 pdf/ 子目录,引用路径: packages/pdf/pdf
# ====== 🔴 原生插件需要ets实现+flutter.har======
# 10. mobile_scanner官方v7.2.0 + 鸿蒙适配合并)
git clone --depth 1 --branch v7.2.0 https://github.com/juliansteenbakker/mobile_scanner.git mobile_scanner
# 合并鸿蒙适配ohos/ 目录、CameraUtil.ets、Barcode.ets 等
```
### 5.3 各包使用示例
@@ -280,30 +254,61 @@ File('report.docx').writeAsBytesSync(DocxGenerator().generate(doc));
File('report.pdf').writeAsBytesSync(PdfGenerator().generate(doc));
```
**pdf专业PDF生成推荐用于PDF导出**
```dart
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
final pdf = pw.Document(
theme: pw.ThemeData.withFont(
base: pw.Font.ttf(fontBytes.buffer.asByteData()),
bold: pw.Font.ttf(boldFontBytes.buffer.asByteData()),
),
);
pdf.addPage(pw.MultiPage(
pageFormat: PdfPageFormat.a4,
build: (context) => [
pw.Text('标题', style: pw.TextStyle(fontSize: 24, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 16),
pw.Text('正文内容'),
pw.TableHelper.fromTextArray(
headers: ['列1', '列2'],
data: [['数据1', '数据2']],
),
],
));
final bytes = await pdf.save();
File('report.pdf').writeAsBytesSync(bytes);
```
---
## 六、项目依赖兼容性总览
| 依赖 | 来源 | Web | 鸿蒙 | 备注 |
|------|------|-----|------|------|
| fl_chart | 本地 path | ✅ | ✅ | 纯 Dart |
| badges | 本地 path | ✅ | ✅ | 纯 Dart |
| flutter_staggered_grid_view | 本地 path | ✅ | ✅ | 纯 Dart |
| cached_network_image | 本地 path | ✅ | ✅ | 条件导入IO 分支 |
| flutter_markdown_plus | 本地 path | ✅ | ✅ | 纯 Dart,全平台支持 |
| flutter_card_swiper | 本地 path | ✅ | ✅ | 纯 Dart |
| qr | 本地 path | ✅ | ✅ | 纯 DartQR码生成 |
| mailer | 本地 path | ❌ | ✅ | 纯 DartSMTP客户端Web不支持 |
| mobile_scanner | 本地 path | ✅ | ✅ | 原生插件,扫码/二维码v7.2.0+鸿蒙适配 |
| docs_gee | 本地 path | ⚠️ | ✅ | 纯DartDOCX/PDF生成Web需处理文件保存 |
| hive_ce | pub.dev | ✅ | ✅ | 纯 Dart |
| get / dio / logger / intl | pub.dev | ✅ | ✅ | 纯 Dart |
| shared_preferences | pub.dev | ✅ | ✅ | localStorage |
| path_provider | git(鸿蒙版) | ⚠️ | ✅ | 需验证 web |
| connectivity_plus | git(鸿蒙版) | ⚠️ | ✅ | 需验证 web |
| share_plus | git(鸿蒙版) | ⚠️ | ✅ | 需验证 web |
| permission_handler | git(鸿蒙版) | | ✅ | Web 不支持 |
| fluttertoast | 本地 path | ⚠️ | ✅ | 需验证 web |
| 依赖 | 来源 | Web | 鸿蒙 | 类型 | 备注 |
|------|------|-----|------|------|------|
| fl_chart | 本地 path | ✅ | ✅ | 🟢纯Dart | 图表 |
| badges | 本地 path | ✅ | ✅ | 🟢纯Dart | 徽标 |
| flutter_staggered_grid_view | 本地 path | ✅ | ✅ | 🟢纯Dart | 瀑布流 |
| cached_network_image | 本地 path | ✅ | ✅ | 🟢纯Dart | 图片缓存 |
| flutter_markdown_plus | 本地 path | ✅ | ✅ | 🟢纯Dart | Markdown |
| flutter_card_swiper | 本地 path | ✅ | ✅ | 🟢纯Dart | 卡片滑动 |
| qr | 本地 path | ✅ | ✅ | 🟢纯Dart | QR码生成 |
| mailer | 本地 path | ❌ | ✅ | 🟢纯Dart | SMTP发邮件 |
| docs_gee | 本地 path | ⚠️ | ✅ | 🟢纯Dart | DOCX/PDF生成 |
| **pdf** | 本地 path | | ✅ | 🟢纯Dart | 专业PDF生成推荐 |
| mobile_scanner | 本地 path | ✅ | ✅ | 🔴原生插件 | 扫码 |
| file_picker | 本地 path | ✅ | ✅ | 🔴原生插件 | 文件选择 |
| fluttertoast_ohos | 本地 path | ⚠️ | ✅ | 🔴原生插件 | Toast提示 |
| hive_ce | pub.dev | | | 🟢纯Dart | 数据库 |
| get / dio / logger / intl | pub.dev | | ✅ | 🟢纯Dart | 工具 |
| shared_preferences | pub.dev | | ✅ | 🟢纯Dart | 存储 |
| path_provider | git(鸿蒙版) | ⚠️ | ✅ | 🔴原生插件 | 路径 |
| connectivity_plus | git(鸿蒙版) | ⚠️ | ✅ | 🔴原生插件 | 网络 |
| share_plus | git(鸿蒙版) | ⚠️ | ✅ | 🔴原生插件 | 分享 |
| permission_handler | git(鸿蒙版) | ❌ | ✅ | 🔴原生插件 | 权限 |
---
@@ -311,6 +316,7 @@ File('report.pdf').writeAsBytesSync(PdfGenerator().generate(doc));
| 文档 | 用途 |
|------|------|
| [**Flutter包鸿蒙适配通用指南.md**](./Flutter包鸿蒙适配通用指南.md) | **👈 通用方法论:判断包类型、适配步骤、踩坑记录、检查清单(给其他人看)** |
| [ohos平台适配flutter三方库指导.md](./ohos平台适配flutter三方库指导.md) | 原生插件完整适配流程MethodChannel、打 har 包) |
| [开发FFI plugin.md](./开发FFI plugin.md) | FFI 插件开发指南 |
| [OpenHarmony应用如何集成Flutter模块.md](./OpenHarmony应用如何集成Flutter模块.md) | Flutter 模块集成到鸿蒙应用 |

View File

@@ -0,0 +1,326 @@
# 鸿蒙(HarmonyOS) 分层图标配置指南
> **适用场景**: 华为应用审核要求配置分层图标(前景图 + 后景图),标准尺寸 1024×1024px
> **更新时间**: 2026-04-25
> **关联脚本**: [gen_hmos_icons.py](../scripts/gen_hmos_icons.py)
---
## 一、为什么需要分层图标?
华为应用上架审核会检查以下警告:
```
为了提供更好的应用启动体验,请使用分层图标。
应用未配置图标的前景图和后景图标准要求尺寸1024px*1024px
```
**原因**: 鸿蒙系统支持"分层图标"机制,将图标拆分为前景层 + 背景层,系统可动态组合:
- 桌面主题切换时自动替换背景色
- 深色/浅色模式自适应
- 不同设备形态统一视觉
---
## 二、规范要求
### 2.1 图片规格
| 属性 | 前景图 (foreground) | 背景图 (background) |
|------|---------------------|---------------------|
| 尺寸 | **1024 × 1024 px** | **1024 × 1024 px** |
| 格式 | PNG | PNG |
| 透明度 | ✅ 支持(非核心区域透明) | ❌ **禁止透明像素** |
| 圆角 | ❌ 不要手动加圆角 | ❌ 不要手动加圆角 |
| 内容 | App 图标主体Logo/图形) | 纯色或简单渐变 |
### 2.2 设计要点
```
┌─────────────────────┐
│ │ ← 四角100% 透明(系统自动裁切圆角)
│ ┌───────────┐ │
│ │ │ │ ← 前景:保留核心图形
│ │ APP LOGO │ │
│ │ │ │
│ └───────────┘ │
│ │
└─────────────────────┘
┌─────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░│ ← 背景:纯色填充,无任何透明区域
│░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░│ (建议与前景主色调一致)
│░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░│
└─────────────────────┘
```
### 2.3 常见错误
| 错误 | 说明 | 正确做法 |
|------|------|---------|
| 背景含透明像素 | 审核直接拒绝 | 导出为不透明纯色 PNG |
| 手动加圆角 | 与系统裁切冲突 | 保持正方形,系统自动处理 |
| 尺寸不是 1024 | 不符合规范 | 必须精确 1024×1024 |
| 使用单层图标 | 触发警告 | 改用 foreground + background 双层 |
---
## 三、文件结构
### 3.1 需要的文件
```
ohos/
├── AppScope/
│ └── resources/
│ └── base/
│ └── media/
│ ├── background_icon.png # 背景层(纯色)
│ ├── foreground_icon.png # 前景层透明PNG
│ ├── layered_image.json # ⭐ 分层配置文件
│ └── app_icon.png # 单层备用图标startWindowIcon 用)
└── entry/
└── src/main/resources/base/media/
├── background_icon.png # 同上entry 目录副本)
├── foreground_icon.png # 同上
├── layered_image.json # 同上
└── icon.png # 启动窗口图标(实际图片,非 JSON
```
### 3.2 关键:`layered_image.json` 格式
⚠️ **这是最容易出错的地方!必须包含外层 `"layered-image"` 键**
```json
{
"layered-image": {
"foreground": "$media:foreground_icon",
"background": "$media:background_icon"
}
}
```
**常见错误格式**(❌ 不识别):
```json
// ❌ 缺少外层键 — 系统无法解析
{
"foreground": "$media:foreground_icon",
"background": "$media:background_icon"
}
// ❌ 多余字段 — 不符合规范
{
"foreground": "...",
"background": "...",
"size": { "width": 1024, "height": 1024 }
}
```
---
## 四、配置引用
### 4.1 app.json5全局应用图标
```json5
// ohos/AppScope/app.json5
{
"app": {
"bundleName": "cute.major.kitchen",
"vendor": "微风暴",
"versionCode": 26042001,
"versionName": "1.1.0",
"icon": "$media:layered_image", // ← 引用分层配置
"label": "$string:app_name"
}
}
```
### 4.2 module.json5入口 Ability 图标)
```json5
// ohos/entry/src/main/module.json5
{
"module": {
"abilities": [
{
"name": "EntryAbility",
"icon": "$media:layered_image", // ← 桌面图标:分层配置
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon", // ← 启动窗口:实际 PNG 图片!
"startWindowBackground": "$color:start_window_background",
"skills": [...]
}
]
}
}
```
### 4.3 字段区别(重要!)
| 字段 | 引用类型 | 说明 |
|------|---------|------|
| `icon` | `$media:layered_image` | 桌面/应用列表图标 → **JSON 配置** |
| `startWindowIcon` | `$media:icon` | 启动闪屏图标 → **实际 PNG 图片** |
⚠️ `startWindowIcon` 不能指向 `layered_image.json`,必须是真实图片文件!
---
## 五、Python 自动生成脚本
### 5.1 脚本位置
[scripts/gen_hmos_icons.py](../scripts/gen_hmos_icons.py)
### 5.2 功能说明
从源图标 `assets/icons/icon_1024x1024.png` 自动提取:
1. **背景层** (`background_icon.png`) — 采样源图标四角颜色,生成纯色背景
2. **前景层** (`foreground_icon.png`) — 抠去背景色,保留图标主体,添加圆角遮罩
3. **配置文件** (`layered_image.json`) — 生成标准格式的分层配置
### 5.3 工作原理
```
源图标 icon_1024x1024.png
┌──────────────┐
│ 采样背景颜色 │ → 从10个角落/边缘点取平均色
└──────┬───────┘
┌────┴────┐
▼ ▼
┌───────┐ ┌──────────┐
│背景层 │ │ 前景层 │
│纯色填充│ │ 去背+圆角 │
│ 5KB │ │ 1372KB │
└───────┘ └──────────┘
│ │
└────┬────┘
layered_image.json
(标准格式输出)
```
### 5.4 运行方式
```bash
# 前置依赖
pip install Pillow
# 运行脚本
python scripts/gen_hmos_icons.py
```
### 5.5 输出示例
```
源图标: assets/icons/icon_1024x1024.png
尺寸: (1024, 1024)
背景色采样: RGB(237, 189, 109)
--- AppScope/resources/base/media ---
background: .../background_icon.png (5.2KB)
颜色: RGB(237, 189, 109)
foreground: .../foreground_icon.png (1371.8KB)
layered_image.json: .../layered_image.json
--- entry/src/main/resources/base/media ---
background: .../background_icon.png (5.2KB)
foreground: .../foreground_icon.png (1371.8KB)
layered_image.json: .../layered_image.json
完成!
```
### 5.6 参数调整
如需自定义,修改脚本顶部常量:
```python
SOURCE_ICON = "assets/icons/icon_1024x1024.png" # 源图标路径
OUTPUT_SIZE = 1024 # 输出尺寸
# generate_foreground() 中:
# tolerance=50 # 背景色容差(越小越严格抠图)
# radius=int(OUTPUT_SIZE * 0.22) # 圆角半径0.22 ≈ 华为默认圆角比例)
```
---
## 六、手动制作流程(备选方案)
如果不想用脚本,可以手动在 Figma / Photoshop / Sketch 中操作:
### 步骤 1: 制作背景图
1. 新建 1024×1024 画布
2. 填充纯色(取自原图主色调)
3. 导出为 `background_icon.png`(确保无透明)
### 步骤 2: 制作前景图
1. 打开原图 1024×1024
2. 删除/隐藏背景图层,只保留图形主体
3. **不要加圆角**
4. 导出为 `foreground_icon.png`(保留透明通道)
### 步骤 3: 配置 JSON
创建 `layered_image.json`(见第三章 3.2
### 步骤 4: 放置文件
按第三章 3.1 结构放入对应目录
---
## 七、验证清单
部署前逐项检查:
- [ ] `background_icon.png` 存在于两个 media 目录
- [ ] `foreground_icon.png` 存在于两个 media 目录
- [ ] `layered_image.json` 格式正确(有 `"layered-image"` 外层键)
- [ ] `app.json5``"icon"` 指向 `$media:layered_image`
- [ ] `module.json5``"icon"` 指向 `$media:layered_image`
- [ ] `module.json5``"startWindowIcon"` 指向 `$media:icon`(实际图片)
- [ ] 两张图片均为 **1024×1024** PNG
- [ ] 背景图**不含任何透明像素**
- [ ] 前景图**没有手动圆角**
---
## 八、常见问题排查
### Q1: 配置后仍有警告?
**检查优先级**: `module.json5` 的 icon 会覆盖 `app.json5`。如果 module.json5 配了旧的单层图标引用,全局配置不会生效。
**解决**: 确保 `module.json5` 中 abilities 的 `icon` 也改为 `$media:layered_image`
### Q2: 启动时图标空白?
**原因**: `startWindowIcon` 指向了 `layered_image.json`(这是 JSON 不是图片)。
**解决**: `startWindowIcon` 必须指向实际 PNG 文件,如 `$media:icon`
### Q3: DevEco Studio 版本要求?
华为要求 **DevEco Studio ≥ 5.0.5.315** 进行图标处理。低版本升级后可能有残留配置,需清理后重新打包。
### Q4: 图标显示模糊/有锯齿?
- 确保前景图透明区域是 **100% 透明**alpha=0不是半透明
- 使用标准 PNG 格式,避免非标准转换导致失真
- 源图分辨率至少 1024×1024
---
## 九、参考链接
- [华为官方:配置应用图标和名称](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/layered-image)
- [OpenHarmony 文档:分层图标](https://github.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/layered-image.md)
- [华为开发者社区:分层图标 FAQ](https://developer.huawei.com/consumer/cn/forum/topic/0203201129389706916)