先说个背景。学校勤工俭学中心这个季度新增了一个需求:在 App 首页顶部展示勤工俭学岗位的轮播横幅,比如“图书馆助理招募中”“食堂帮厨急聘”这类信息。听起来不难,但要命的是,负责这个项目的单位既要快速上线,又要兼容手头已有的 OpenHarmony 设备——大部分是老款 RK3568 开发板改造的展示终端,还有一部分学生会自备手机,安卓和 iOS 都有。技术负责人当时一句话定了方向:用 Flutter 做跨端,OpenHarmony 那边正好有官方适配,先拿这个顶部横幅做第一个试点。于是就有了这篇实战记录。
这篇文章我会从项目背景、环境搭建、核心实现、原生交互到问题排查,完整过一遍 Flutter × OpenHarmony 跨端开发的过程。最终交付的是一个可复用的顶部横幅轮播组件,以及一整套从零到一跑通跨端项目的实操经验。适合两类人看:一类是刚接触 Flutter 和 OpenHarmony 跨端开发、想找个真实项目下手的同学;另一类是已经在做 OpenHarmony 应用,但被工具链、设备调试、原生插件折腾得头疼的一线开发。
1. 项目背景与整体设计思路
1.1 为什么选 Flutter 而不是其他跨端方案
勤工俭学这个场景有一个很现实的特点:终端设备极其杂乱。学校已有的展示终端是 OpenHarmony 系的 RK3566/RK3568 开发板,学生自己的手机则是安卓苹果混合,有些学生的安卓版本还比较老。如果按传统做法,一套原生 OpenHarmony 代码加一套安卓代码加一套 iOS 代码,维护成本直接翻三倍,而实际业务逻辑却简单得可怜——拉取岗位列表、定时轮播、点击跳转。
跨端方案里,React Native 虽然也有 OpenHarmony 适配,但当时对应 React Native 的 OpenHarmony 版本更新节奏不稳定,社区资料也少。Flutter 的情况要好一些,OpenHarmony SIG 组维护的 flutter_flutter 仓库一直在跟进上游版本,而且在图形渲染层面,Flutter 自研的 Skia 渲染引擎和 OpenHarmony 的图形栈兼容性做得比较细。换句话说,Flutter 在 OpenHarmony 上跑 UI 动画,流畅度比一般的 webview 套壳或者 RN 桥接方案更接近原生体验。
如果只是做一个顶部横幅,确实用不着跨端框架,但考虑到后续勤工俭学平台还要上报名、审核、签到这些模块,第一步就得选一个能长期打的基础框架。Flutter 的生态、状态管理、组件库都很成熟,社区里踩坑记录也多,这是比较稳妥的路线。
1.2 顶部横幅在业务中的具体位置
先明确一下这个横幅在勤工俭学 App 里的层级关系:首页是一个普通的商品/信息流页面,顶部横幅只是其中一个区域组件,展示的数据是“勤工俭学岗位推荐”。这个组件需要支持以下能力:
- 自动轮播,间隔可配置,默认 4 秒
- 支持无限循环,避免滑到尽头后回弹的生硬体验
- 点击横幅跳转到岗位详情页
- 轮播状态与 App 前后台切换联动:App 退后台自动暂停,回前台自动恢复
- 数据从服务端获取,但要有本地缓存,网络不好时先用旧数据渲染
这些需求单看都不难,但如果放在 Flutter × OpenHarmony 的跨端场景里,每一件小事都要考虑两个平台的差异点。比如生命周期控制,Flutter 在 Android 里监听 AppLifecycleState 很顺畅,但在 OpenHarmony 上,由于应用层和 Flutter engine 的接入方式不一样,退后台的事件有时会延迟甚至丢失,这里需要额外处理。
1.3 整体技术架构
整个工程采用 Flutter 作为 UI 层和业务逻辑层,OpenHarmony 侧仅保留必要的原生能力调用和系统壳工程。架构上分了四层:
code复制┌─────────────────────────────────────────────┐
│ 业务层:勤工俭学列表 / 横幅数据模型 / 页面跳转 │
├─────────────────────────────────────────────┤
│ Flutter 组件层:BannerView / 轮播逻辑 / 缓存 │
├─────────────────────────────────────────────┤
│ 平台通道层:MethodChannel / EventChannel │
├─────────────────────────────────────────────┤
│ 原生层:OpenHarmony Ability / 系统能力调用 │
└─────────────────────────────────────────────┘
业务层全部写在 Flutter 侧,原生壳只做最小化处理。这样做的好处是,后续就算要换掉 OpenHarmony 设备,比如把展示终端全部换成标准安卓盒子,壳工程重写一遍,Dart 代码一行都不用动。多端共用一个 Flutter 工程,每一端只维护一个轻量壳工程,这是跨端项目最理想的状态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境搭建与工程初始化
2.1 开发环境版本选型
跨端开发最先卡人的往往不是代码,而是环境。Flutter 的 OpenHarmony 适配版本不像标准 Flutter 那样大而全,它是 OpenHarmony SIG 组维护的一个独立分支,安装方式也不同。我这次用的是以下组合,实测跑通了整个流程:
| 组件 | 版本/说明 |
|---|---|
| Flutter SDK | OpenHarmony 分支 3.7.12(SIG 维护版) |
| OpenHarmony SDK | API 10(DevEco Studio 内置) |
| DevEco Studio | 4.0 及以上 |
| hdc 工具 | OpenHarmony 命令行工具,用于设备连接与日志 |
| 真机 | 润和 RK3568 开发板 / 标准 OpenHarmony 手机 |
有一点尤其要注意,OpenHarmony 分支的 Flutter SDK 不能直接用官方 Flutter SDK 替代,二者的 engine 和 embedder 实现不一样。如果你在 pubspec.yaml 里能正常拉取依赖,但一编译到 OpenHarmony 设备就报找不到某些 native 符号,大概率是 SDK 用错了。
2.2 环境配置的三个关键步骤
第一步,配置 OpenHarmony 版 Flutter SDK。
我是在 mac 上开发的,网上关于“mac flutter开发环境搭建”的资料很多,但大多是针对标准 Flutter。OpenHarmony 分支需要单独 clone:
bash复制git clone https://gitee.com/openharmony-sig/flutter_flutter.git -b 3.7.12
然后把 flutter_flutter/bin 加进 PATH,或者直接用完整路径调用。这里容易踩坑的点是环境变量覆盖——如果你系统里之前装过标准 Flutter,终端里先执行的是哪个,决定了你后面用的到底是谁。建议用一个专门的外层目录,把 OpenHarmony 版 Flutter 和其他工具隔离,减少混乱。
第二步,配置 DevEco Studio 的 OpenHarmony SDK。
DevEco Studio 是 OpenHarmony 应用开发的主 IDE,和 Android Studio 类似。首次打开会自动下载 SDK,国内网络环境下可能要等很久,建议提前在官网把 API 10 的 SDK 包下载好,手动解压后配置到 IDE 里。
第三步,确认真机连接。
OpenHarmony 和安卓不同,它的调试命令是 hdc(HarmonyOS Device Connector),不是 adb。开发板通电后,通过 USB 连到电脑,命令行先跑:
bash复制hdc list targets
如果能看到设备号,说明连接正常。很多同学卡在这一步是因为没装 hdc 的驱动,尤其是 RK3568 这类开发板在 mac 上偶尔枚举不到,换个 USB 口或换根数据线说不准就好了,这种小问题有时候最浪费时间。
2.3 查看设备系统版本的正确姿势
开发中经常需要确认真机的 OpenHarmony 系统版本,用来对齐 API 兼容性。hdc 里不像安卓那样直接 adb shell getprop ro.build.version.release,OpenHarmony 的版本信息是存在系统参数里的,要用 param 命令:
bash复制hdc shell param get const.product.name
hdc shell param get const.product.version
hdc shell param get const.ohos.version
第一条拿到的是产品名称,比如 RK3568 开发板会返回类似 RK3568 的字符串;第三条拿到的是 OpenHarmony 系统版本号,比如 3.2.0 或 5.0.0。后面排查问题、确认 API 级别时这个信息非常关键,比如拉起系统能力时发现某个接口报权限不足,查来查去结果是系统版本比预期低,参数信息一看就明白了。
这个命令系列在面试或技术交流里也常被问到,属于 OpenHarmony 开发的基础操作。别看它简单,能不能快速确认设备信息,直接决定了排查问题的效率。
2.4 Gradle 插件报错三连
创建 Flutter OpenHarmony 工程后,第一次编译时最容易遇到三个 Gradle 报错,我在热搜词里也看到大量同学在搜同样的问题,这里直接一起给出解决方案。
第一个报错是:
code复制You are applying Flutter's main Gradle plugin imperatively using the apply script
这是 Flutter 插件脚本在 OpenHarmony 工程里默认写法不兼容导致的。Flutter 官方插件新的推荐写法是用 plugins {} DSL 块,而不是旧的 apply 方式。解决办法是打开 ohos/build.gradle,把:
gradle复制apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
改成:
gradle复制plugins {
id "dev.flutter.flutter-plugin-loader"
}
顺便检查 settings.gradle 里有没有声明对应的 pluginManagement 仓库。
第二个报错是:
code复制Error resolving plugin [id: 'dev.flutter.flutter-plugin-loader', version: ...]
这个多半是 Flutter SDK 的版本和插件的版本对不上。OpenHarmony SIG 分支里,flutter-plugin-loader 插件版本和 SDK 版本是严格对应的,不能随意混用。检查 ohos/settings.gradle 里的 pluginManagement 仓库,确认包含了 gitee 和 maven central 的地址,再把插件版本改成和 SDk 分支一致即可。
第三个报错是编译到一般时提示缺少 OpenHarmony SDK 的某个 platform 目录。这个通常是 DevEco Studio 里下载的 SDK 不完整,或者本地配置的 SDK 路径和工程要求的 API Level 不一致。建议在 DevEco Studio 里重新配置一下本地 SDK,确保工程 build-profile.json5 里的 compileSdkVersion 和 compatibleSdkVersion 在你本地确有对应版本。
3. 顶部横幅组件的核心实现
3.1 Flutter 侧的 UI 拆解
Banner 组件本身在 Flutter 里实现并不复杂,关键在于设计得够不够通用。我做的是一个可复用的 JobBannerView,数据结构上绑定了勤工俭学岗位模型:
dart复制class JobPost {
final String id;
final String title;
final String imageUrl;
final String redirectUrl;
JobPost({
required this.id,
required this.title,
required this.imageUrl,
required this.redirectUrl,
});
factory JobPost.fromJson(Map<String, dynamic> json) {
return JobPost(
id: json['id'].toString(),
title: json['title'] as String,
imageUrl: json['image_url'] as String,
redirectUrl: json['redirect_url'] as String? ?? '',
);
}
}
UI 上直接用 PageView 实现横向翻页轮播,每页显示一张大图,图片底部叠一层半透明渐变,再放上岗位标题,右下角显示“查看详情”文字。为了不引入额外依赖,图片加载用的是 Image.network,但做了本地缓存封装——第一次加载成功后把图片文件写到临时目录,下次优先读本地文件,断网也能展示。
顶部横幅不需要太花哨的动画,重点要稳。我用 Timer.periodic 控制自动翻页,每 4 秒调用一次 PageController.nextPage。关键在于“无限循环”的实现:PageView 的 itemCount 设成一个很大的数,比如 10000,初始 index 设为 5000,这样用户无论向左还是向右滑动,都不会滑到边界导致回弹。每次翻页时用 currentPage % 列表长度 取到真实的数据下标。
3.2 生命周期管理:停在后台就刹车
横幅轮播有一个非常容易忽略的细节:App 退到后台,Timer 不会自动停。如果用户切到后台十分钟,回来发现横幅已经翻了一百多页,体验很差,还白白耗电。所以必须在生命周期变化时暂停和恢复轮播。
Flutter 里通常在 WidgetsBindingObserver 的 didChangeAppLifecycleState 里处理:
dart复制@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_startAutoPlay();
} else {
_stopAutoPlay();
}
}
但是在 OpenHarmony 端,这里有几个隐藏问题。OpenHarmony 的分层架构里,Flutter engine 是在 ohos 壳工程的 Ability 生命周期下运行的,退后台时 Flutter侧收到的生命周期回调可能延迟。为了兜底,我在原生侧也做了处理:Ability 的 onBackground 和 onForeground 回调里,通过 MethodChannel 给 Flutter 侧发一个显式的事件,Flutter 收到事件后再决定暂停还是恢复。
这种“双保险”思路在跨端开发里非常实用。你不能假设任何一端的生命周期事件 100% 可靠,宁可冗余一点,也好过在线上环境中出现“轮播停不下来”的诡异 bug。
3.3 从服务端拉取数据与本地缓存
勤工俭学的岗位数据来自一个简单的 JSON 接口,GET /api/job/banner。Flutter 侧用自封装的 HttpClient 拉取,超时时间设 8 秒,错误时静默降级到本地缓存数据。
为了减少网络请求依赖,我在本地用 shared_preferences 存放最近一次的 JSON 字符串,每次启动先渲染缓存,再在后台拉新数据,拉到成功后更新 UI 并覆盖缓存。这种模式在数据量小、更新不频繁的场景下非常合适,用户总是先看到内容,再等刷新,感知上会快很多。
封装一个数据仓库:
dart复制class BannerRepository {
static const _cacheKey = 'banner_cache';
Future<List<JobPost>> loadBanners() async {
final cached = await _readCache();
if (cached != null) return cached;
final remote = await _fetchFromServer();
if (remote.isNotEmpty) await _writeCache(remote);
return remote;
}
}
_fetchFromServer 内部做了异常捕获,网络异常时不抛错,返回空列表,组件层拿到空列表就渲染一个占位图,不白屏。
3.4 横幅数据与状态管理
这个项目的状态管理我没有引入 provider 或 riverpod,对单个横幅组件来说,StatefulWidget 自带状态完全够用。但考虑到后续勤工俭学 App 整体开发,还是建议尽早引入一个状态管理方案。我目前用的比较顺手的是 Provider + ChangeNotifier,结构清晰,团队上手成本也低。
横幅组件对外暴露一个简单 API:
dart复制class JobBannerView extends StatefulWidget {
final Future<List<JobPost>> Function() future;
final Duration autoPlayInterval;
final void Function(JobPost post) onTap;
}
调用方只要传入拉数函数和点击回调,组件内部自己处理加载、缓存、轮播、生命周期,不需要关心实现细节。这种组件化的思路,对团队协作来说非常关键:写组件的人关注内部实现,用组件的人只关心输入输出。
4. OpenHarmony 端集成与原生能力调用
4.1 创建 OpenHarmony 壳工程并挂载 Flutter Module
这一部分很多初次接触 Flutter + OpenHarmony 的同学不太理解。Flutter 在 OpenHarmony 上并不是像在安卓那样直接运行一个 FlutterActivity,而是通过一个原生壳工程承载 Flutter 渲染层。工程根目录下有 ohos/ 目录,里面的结构和标准 OpenHarmony 应用工程保持一致,包含 entry、hvigor 配置等。
创建项目时,如果用 Flutter CLI 创建:
bash复制flutter create --platforms ohos job_flutter_app
会在工程里直接生成 ohos/ 目录。如果没有这个目录,说明你的 Flutter SDK 版本不是 OpenHarmony 分支的,换个 SDK 再试。
OHOS 壳工程里的 entry/src/main/module.json5,需要注册一个专门的 Ability 来承载 Flutter,一般默认是 entry 模块里的 MainAbility。主入口里调用:
dart复制// 原生侧
import ohos_flutter_ohos from 'flutter_ohos';
hiAppEntry.onCreate(this);
这里的渲染和生命周期绑定都在原生侧完成,Dart 代码只是业务逻辑。整个链路可以理解成:OpenHarmony Ability → Flutter engine 绑定 → Dart UI 渲染。
4.2 MethodChannel 打通原生与 Flutter
勤工俭学顶部横幅虽然 UI 层很轻,但有一个场景必须要走原生通道:获取设备的网络状态和电量。横幅展示时如果设备电量低于 15%,需要一个半透明的“设备低电量”角标提示运维人员及时充电;如果网络切换为 Wi-Fi 或蜂窝,需要刷新一次数据。这些系统能力 Flutter 没有直接 API,必须由原生侧负责查询。
Flutter 侧定义通道:
dart复制static const MethodChannel _channel = MethodChannel('school/job_banner');
Future<Map<dynamic, dynamic>> fetchDeviceInfo() async {
try {
final result = await _channel.invokeMethod('getDeviceInfo');
return result;
} on PlatformException catch (e) {
print('获取设备信息失败: ${e.message}');
return {'battery': 100, 'networkType': 'unknown'};
}
}
OpenHarmony 原生侧对应实现:
java复制import ohos.rpc.IRemoteObject;
import ohos.rpc.MessageOption;
import ohos.rpc.MessageParcel;
import ohos.rpc.RemoteObject;
import ohos.utils.zson.ZSONObject;
public class JobBannerService extends RemoteObject implements IRemoteBroker {
@Override
public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
// 解析请求,返回设备信息 ZSON 对象
}
}
这种基于通道的原生交互方式,在 Flutter 跨端里非常常见,OpenHarmony 版保持了对齐。如果后续需要频繁双向通信,比如原生侧主动推送设备状态,可以改用 EventChannel,原理类似,Flutter 侧通过 receiveBroadcastStream() 监听即可。
4.3 在 OHOS 侧调用系统能力设置横幅角标
再举一个具体的原生能力调用例子。顶部横幅右上角要显示一个“校园 Wi-Fi”的标志,这个小标志不是静态图片,而是根据系统当前网络类型动态变化的。如果设备连着校园 Wi-Fi,显示 Wi-Fi 图标;如果用的是以太网或蜂窝,显示对应图标。
原生侧用 OpenHarmony 的 @ohos.net.connection 能力获取网络类型:
typescript复制import connection from '@ohos.net.connection';
function getNetworkTypeSync(): string {
let netHandle = connection.getDefaultNet();
let capabilities = connection.getNetCapabilities(netHandle);
if (capabilities.bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
return 'wifi';
}
if (capabilities.bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
return 'cellular';
}
return 'ethernet';
}
拿到网络类型后,通过 MethodChannel 返回给 Flutter,组件再决定渲染哪个图标。跨端开发中类似这样“调用系统能力”的需求特别多,核心思路是:Flutter 只负责 UI 表达和业务状态,系统相关的 query 和监听全部放原生侧。
5. 常见问题与排查技巧实录
5.1 让 RK3568 开发板联网失败
这次项目有一部分真机是 RK3568 开发板,插了网线但应用里始终拉不到数据。排查下来发现是权限问题,OpenHarmony 应用默认没有网络访问权限,需要在 module.json5 里显式声明:
json复制{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
这点和安卓的 Manifest.permission.INTERNET 声明是一回事。很多跨端开发者习惯在安卓端写好了权限,到了 OpenHarmony 端忘掉,导致线上环境无声失败。
5.2 MediaCodecVideoRenderer 渲染报错
横幅里有一张动图,用的是一段循环播放的小视频,在 OpenHarmony 设备上播放时频繁报 MediaCodecVideoRenderer error。这个问题的根因是 OpenHarmony 自带的媒体编解码能力和安卓不完全一致,某些编码格式或分辨率规格支持不到位。
我的处理办法是放弃视频方案,把动图转成 WebP 序列帧,由 Flutter 端用定时器循环切换图片实现类似播放的效果。这在顶部横幅这种轻量场景里完全够用,避免踩编解码的深坑。经验是:做跨端时尽量用基础的、普适的媒体格式,越通用的格式在不同平台上的兼容性越好。
5.3 低电量角标在部分设备上不显示
刚上线的版本里,低电量角标在部分机型上不显示,排查后发现是电池信息读取接口在不同系统版本上行为不一致。有的版本能直接返回电量百分比,有的版本需要先注册电量监听才能拿到实时值。我统一改成注册监听后动态更新,解决了各设备差异的问题。
5.4 横幅点击在 OpenHarmony 上出现 200ms 延迟
这个是一个比较隐蔽的性能问题。点击横幅跳转详情页时,OpenHarmony 设备上明显感觉到一次“卡顿”然后才跳转,安卓设备上却没这么明显。原因是 OpenHarmony 壳工程里默认禁用了点击事件的预测,Flutter 侧在 Android 端有触控预测辅助,OpenHarmony 端没做对应优化。
解决方式是修改原生壳工程里 FlutterView 的触控事件配置,开启快速响应模式,同时减少 Dart 侧点击跳转前的无谓耗时操作。其实这不算代码 bug,更像是平台差异导致的体验问题,跨端开发里这种“一个平台流畅一个平台卡”的情况特别值得注意。
5.5 遇到 SDK 编译残留目录问题怎么办
开发过程中会积累大量中间产物,尤其是 OpenHarmony 工程的 build、.hvigor、oh_modules 这些目录,有时候会干扰到编译,出现各种诡异报错。可以手动清理:
bash复制hdc shell rm -rf /data/app/el1/100/public/com.example.jobapp
hvigorw clean
如果还不行,就删掉工程根目录下的 .hvigor 和 oh_modules,重新 sync。注意到编译安装到开发板后,如果代码更新不生效,多半是奇安装残留覆盖不完整,卸载重装一次往往就好了。这个问题我在 RK3568 上碰到过很多次,已经成为每次提测前的固定“祭天”项目了。
5.6 常见问题速查表
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| Flutter 插件 resolve 失败 | SDK 版本和插件版本不匹配 | 确认 OpenHarmony Flutter 分支版本,重新配置 settings.gradle |
| hdc 连不上设备 | 驱动缺失/数据线问题 | 换 USB 口、换线、重装驱动 |
| OpenHarmony 设备上网络请求失败 | 未声明 INTERNET 权限 | 在 module.json5 中加权限声明 |
| 退后台后轮播不停 | 生命周期回调未监听 | 双保险:原生+Flutter 侧都做生命周期派发 |
| 原生端图片显示不出来 | 编码格式不兼容 | 统一转成 JPEG/WebP |
| 设备信息获取为空 | 系统版本接口差异 | 改为监听模式,动态更新 |
6. 实操总结与后续扩展建议
6.1 这个项目让我踩透了跨端开发的“平台差异”
做完这个顶部横幅,最大的感受是:跨端开发真正的难点不在业务逻辑,而在平台差异的“碎片化”。Flutter 帮你抹平了 UI 渲染层的差异,但生命周期、系统能力、性能调度这些原生的东西,Flutter 是包不住的,你必须对每一端的原生特性都有一定了解。
有些细节是最容易出事的:OpenHarmony 的权限模型和安卓并不完全等价,后台任务限制更加严格,某些系统接口在模拟器上能跑但真机上报错。这些只能靠多做真机测试来踩平,没有捷径。
6.2 对 Flutter × OpenHarmony 技术栈的一些看法
OpenHarmony 的 Flutter 适配还在快速迭代中,API 版本推进比较快,新版本 SDK 对老工程的兼容性并不总是完美。如果项目要长期维护,我会建议锁定一套经过验证的 SDK 版本组合,不要盲目追新。同时,要把官方的 flutter_flutter 仓库和 issue 列表作为日常信息来源,很多平台兼容性问题在这里能找到官方回应。
社区里有很多活跃在 Gitee 和开发者论坛上的同行,遇到奇怪问题搜一遍可能就有答案。跨端项目的核心还是“自动刹车”——尽量把依赖锁定在可控范围里。
6.3 后续可以怎么扩展
顶部横幅跑通之后,勤工俭学平台后续可以继续扩展报名、签到、工时统计等模块,整体架构保持在 Flutter 侧。原生侧需要新增的能力(比如扫码签到、蓝牙打印)走同样的 MethodChannel 模式,每个新能力加一个独立的 service 类,Flutter 侧按业务模块分包,接口清晰、代码也好维护。
如果将来还要接新的硬件设备,比如智能考勤机,思路是一样的:硬件厂商通常提供 SDK,你只需要在 OpenHarmony 侧写一个 adapter,封装成标准能力接口给 Flutter 调用。跨端框架能不能真正“一套代码多端运行”,取决于这一层 adapter 写得好不好。
这个项目虽然只是一个小小的顶部横幅,但麻雀虽小五脏俱全。单说工程搭建、生命周期处理、原生交互、真机调试、性能优化,就已经把 Flutter × OpenHarmony 跨端开发的主要环节都走了一遍。手头有 OpenHarmony 设备、又想尝试 Flutter 跨端的同学,完全可以拿这个项目做起点,一步步把功能做大。
