- 添加鸿蒙分层图标配置和生成脚本 - 修复数据导出JSON解析问题 - 优化关于页面和团队信息展示 - 更新应用版本至1.4.1 - 清理代码警告和冗余文件 - 添加字体和二维码测试脚本 - 完善鸿蒙适配文档和指南
12 KiB
12 KiB
鸿蒙适配方案
文档创建: 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 拉取源码
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 项目引用 & 验证
# pubspec.yaml
dependencies:
<package_name>:
path: packages/<package_name>
flutter pub get && flutter analyze --no-pub
✅ 完成!
就这么简单! 纯Dart代码跨平台编译,不需要任何原生适配。 不要创建 ohos/ 目录、不要写 Plugin.ets、不要配置 module.json5。
三、原生插件适配流程(参考)
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 | 🟢 纯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 各包克隆命令速查
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
import 'package:fl_chart/fl_chart.dart';
LineChart(LineChartData(...))
badges
import 'package:badges/badges.dart' as badges;
badges.Badge(badgeContent: Text('3'), child: Icon(CupertinoIcons.shopping_cart))
flutter_staggered_grid_view
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
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
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
Markdown(data: '# Hello\n**bold** text', onTapLink: (text, href, title) {})
flutter_card_swiper
import 'package:flutter_card_swiper/flutter_card_swiper.dart';
CardSwiper(itemCount: cards.length, itemBuilder: (ctx, index) => CardWidget(cards[index]), onSwipe: (prev, curr, direction) {})
qr
import 'package:qr/qr.dart';
final qrCode = QrCode(4, QrErrorCorrectLevel.L)..addData('Hello, world!');
final qrImage = QrImage(qrCode);
mailer
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
import 'package:mobile_scanner/mobile_scanner.dart';
MobileScanner(
controller: MobileScannerController(),
onDetect: (result) {
print(result.barcodes.first.rawValue);
},
)
docs_gee
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导出)
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生成 |
| 本地 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 | 👈 通用方法论:判断包类型、适配步骤、踩坑记录、检查清单(给其他人看) |
| ohos平台适配flutter三方库指导.md | 原生插件完整适配流程(MethodChannel、打 har 包) |
| [开发FFI plugin.md](./开发FFI plugin.md) | FFI 插件开发指南 |
| OpenHarmony应用如何集成Flutter模块.md | Flutter 模块集成到鸿蒙应用 |
| FlutterChannel通信.md | MethodChannel / EventChannel / BasicMessageChannel 用法 |