feat: 更新鸿蒙应用配置与功能优化
- 添加鸿蒙分层图标配置和生成脚本 - 修复数据导出JSON解析问题 - 优化关于页面和团队信息展示 - 更新应用版本至1.4.1 - 清理代码警告和冗余文件 - 添加字体和二维码测试脚本 - 完善鸿蒙适配文档和指南
This commit is contained in:
239
packages/Flutter包鸿蒙适配通用指南.md
Normal file
239
packages/Flutter包鸿蒙适配通用指南.md
Normal 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) | 本项目已适配的具体包清单和使用示例 |
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-keep-property-name
|
||||
flutter
|
||||
native*
|
||||
@@ -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}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
Binary file not shown.
@@ -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}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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. */
|
||||
}
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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}
|
||||
@@ -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": []
|
||||
}
|
||||
}
|
||||
@@ -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 | ✅ 纯Dart,DOCX/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(纯Dart,DOCX/PDF文档生成库)
|
||||
# 9. docs_gee(纯Dart,DOCX/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 | ✅ | ✅ | 纯 Dart,QR码生成 |
|
||||
| mailer | 本地 path | ❌ | ✅ | 纯 Dart,SMTP客户端,Web不支持 |
|
||||
| mobile_scanner | 本地 path | ✅ | ✅ | 原生插件,扫码/二维码,v7.2.0+鸿蒙适配 |
|
||||
| docs_gee | 本地 path | ⚠️ | ✅ | 纯Dart,DOCX/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 模块集成到鸿蒙应用 |
|
||||
|
||||
326
packages/鸿蒙分层图标配置指南.md
Normal file
326
packages/鸿蒙分层图标配置指南.md
Normal 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)
|
||||
Reference in New Issue
Block a user