- 添加鸿蒙分层图标配置和生成脚本 - 修复数据导出JSON解析问题 - 优化关于页面和团队信息展示 - 更新应用版本至1.4.1 - 清理代码警告和冗余文件 - 添加字体和二维码测试脚本 - 完善鸿蒙适配文档和指南
324 lines
12 KiB
Markdown
324 lines
12 KiB
Markdown
# 鸿蒙适配方案
|
||
|
||
> 文档创建: 2026-04-09 | 最后更新: 2026-04-25
|
||
> 适配策略: 纯 Dart 包零成本适配 + 原生插件完整适配
|
||
> ⚠️ 重要教训: 纯Dart包禁止添加ohos目录,会导致启动闪退(9001005)
|
||
|
||
---
|
||
|
||
## 一、纯 Dart 包 vs 原生插件判断
|
||
|
||
| 检查项 | 纯 Dart 包 | 原生插件 |
|
||
|--------|-----------|---------|
|
||
| `android/` `ios/` 目录 | ❌ 无 | ✅ 有 |
|
||
| `flutter.plugin` 声明 | ❌ 无 | ✅ 有 |
|
||
| MethodChannel / FFI | ❌ 无 | ✅ 有 |
|
||
| 适配方式 | **仅改版本号,无ohos目录!** | ets 原生实现 + har 包 |
|
||
| 工作量 | 1min | 1-3 天 |
|
||
|
||
### 快速判断流程
|
||
|
||
```
|
||
新包是否包含原生代码?
|
||
├── 否(纯 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 包适配步骤(正确做法)
|
||
|
||
### 2.1 拉取源码
|
||
|
||
```bash
|
||
cd packages
|
||
git clone --depth 1 --branch <version> <github-url> <package-name>
|
||
```
|
||
|
||
### 2.2 修改版本号
|
||
|
||
`pubspec.yaml` 中 `version: x.x.x` → `x.x.x-ohos.1`
|
||
|
||
### 2.3 项目引用 & 验证
|
||
|
||
```yaml
|
||
# pubspec.yaml
|
||
dependencies:
|
||
<package_name>:
|
||
path: packages/<package_name>
|
||
```
|
||
|
||
```bash
|
||
flutter pub get && flutter analyze --no-pub
|
||
```
|
||
|
||
### ✅ 完成!
|
||
|
||
> **就这么简单!** 纯Dart代码跨平台编译,不需要任何原生适配。
|
||
> 不要创建 ohos/ 目录、不要写 Plugin.ets、不要配置 module.json5。
|
||
|
||
---
|
||
|
||
## 三、原生插件适配流程(参考)
|
||
|
||
详见 [ohos平台适配flutter三方库指导.md](./ohos平台适配flutter三方库指导.md)
|
||
|
||
```
|
||
1. flutter create --platforms ohos <plugin>_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. flutter create --platforms ohos example 验证
|
||
```
|
||
|
||
---
|
||
|
||
## 四、Web 兼容性问题备忘
|
||
|
||
项目 Web 白屏与各适配包无关,是项目本身依赖问题:
|
||
|
||
| 问题依赖 | 原因 | 修复方案 |
|
||
|----------|------|---------|
|
||
| `dart:io` (logger_service) | Web 不支持 | 条件导入或 kIsWeb 检查 |
|
||
| `path_provider` (git版) | 可能无 web 实现 | 跳过初始化 |
|
||
| `permission_handler` | Web 不支持原生权限 | stub 或跳过 |
|
||
| `fluttertoast` (本地) | 可能无 web 实现 | SnackBar 替代 |
|
||
|
||
---
|
||
|
||
## 五、已适配包清单
|
||
|
||
### 5.1 总览表
|
||
|
||
| # | 包名 | 类型 | 原版本 | 适配版本 | 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
|
||
|
||
# 2. badges
|
||
git clone --depth 1 --branch v3.2.0 https://github.com/yako-dev/flutter_badges.git badges
|
||
|
||
# 3. flutter_staggered_grid_view
|
||
git clone --depth 1 --branch v0.7.0 https://github.com/letsar/flutter_staggered_grid_view.git
|
||
|
||
# 4. cached_network_image(monorepo,主包在子目录)
|
||
git clone --depth 1 --branch v3.4.1 https://github.com/Baseflow/flutter_cached_network_image.git cached_network_image
|
||
# 引用路径: packages/cached_network_image/cached_network_image
|
||
|
||
# 5. flutter_markdown_plus
|
||
git clone --depth 1 --branch v1.0.7 https://github.com/foresightmobile/flutter_markdown_plus.git flutter_markdown_plus
|
||
|
||
# 6. flutter_card_swiper
|
||
git clone --depth 1 --branch v7.2.0 https://github.com/ricardodalarme/flutter_card_swiper.git flutter_card_swiper
|
||
|
||
# 7. qr
|
||
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. 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 各包使用示例
|
||
|
||
**fl_chart**
|
||
```dart
|
||
import 'package:fl_chart/fl_chart.dart';
|
||
LineChart(LineChartData(...))
|
||
```
|
||
|
||
**badges**
|
||
```dart
|
||
import 'package:badges/badges.dart' as badges;
|
||
badges.Badge(badgeContent: Text('3'), child: Icon(CupertinoIcons.shopping_cart))
|
||
```
|
||
|
||
**flutter_staggered_grid_view**
|
||
```dart
|
||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||
MasonryGridView.count(crossAxisCount: 2, itemCount: items.length, itemBuilder: (ctx, i) => Tile(index: i))
|
||
```
|
||
|
||
**cached_network_image**
|
||
```dart
|
||
import 'package:cached_network_image/cached_network_image.dart';
|
||
CachedNetworkImage(imageUrl: 'url', placeholder: (ctx, url) => CircularProgressIndicator(), errorWidget: (ctx, url, err) => Icon(Icons.error))
|
||
```
|
||
|
||
**flutter_markdown_plus**
|
||
```dart
|
||
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
|
||
Markdown(data: '# Hello\n**bold** text', onTapLink: (text, href, title) {})
|
||
```
|
||
|
||
**flutter_card_swiper**
|
||
```dart
|
||
import 'package:flutter_card_swiper/flutter_card_swiper.dart';
|
||
CardSwiper(itemCount: cards.length, itemBuilder: (ctx, index) => CardWidget(cards[index]), onSwipe: (prev, curr, direction) {})
|
||
```
|
||
|
||
**qr**
|
||
```dart
|
||
import 'package:qr/qr.dart';
|
||
final qrCode = QrCode(4, QrErrorCorrectLevel.L)..addData('Hello, world!');
|
||
final qrImage = QrImage(qrCode);
|
||
```
|
||
|
||
**mailer**
|
||
```dart
|
||
import 'package:mailer/mailer.dart';
|
||
import 'package:mailer/smtp_server.dart';
|
||
final smtpServer = gmail('user@gmail.com', 'password');
|
||
final message = Message()..from = Address('user@gmail.com')..recipients.add('target@example.com')..subject = 'Test'..text = 'Hello';
|
||
final sendReport = await send(message, smtpServer);
|
||
```
|
||
|
||
**mobile_scanner**
|
||
```dart
|
||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||
MobileScanner(
|
||
controller: MobileScannerController(),
|
||
onDetect: (result) {
|
||
print(result.barcodes.first.rawValue);
|
||
},
|
||
)
|
||
```
|
||
|
||
**docs_gee**
|
||
```dart
|
||
import 'package:docs_gee/docs_gee.dart';
|
||
import 'dart:io';
|
||
|
||
final doc = Document(title: '报告', author: '作者');
|
||
doc.addParagraph(Paragraph.heading('标题', level: 1));
|
||
doc.addParagraph(Paragraph.text('正文内容'));
|
||
doc.addTable(Table(rows: [
|
||
TableRow(cells: [TableCell.text('列1'), TableCell.text('列2')]),
|
||
TableRow(cells: [TableCell.text('数据1'), TableCell.text('数据2')]),
|
||
]));
|
||
|
||
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 | ✅ | ✅ | 🟢纯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(鸿蒙版) | ❌ | ✅ | 🔴原生插件 | 权限 |
|
||
|
||
---
|
||
|
||
## 七、参考文档索引
|
||
|
||
| 文档 | 用途 |
|
||
|------|------|
|
||
| [**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 模块集成到鸿蒙应用 |
|
||
| [FlutterChannel通信.md](./如何使用Flutter与OpenHarmony通信%20FlutterChannel.md) | MethodChannel / EventChannel / BasicMessageChannel 用法 |
|