ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于Flutter for OpenHarmony的手语学习App练习中心实战

基于Flutter for OpenHarmony的手语学习App练习中心实战 手语学习App这个方向我关注挺久了。说实话市面上专门做手语教学的App不少但交互体验大多停留在看视频跟学的阶段缺少真正的练习闭环。这次把项目跑在 Flutter for OpenHarmony 上既是想验证开源鸿蒙生态的成熟度也是想把手语学习从单向观看变成学-练-测-复习的完整流程。这个项目要解决的痛点很明确手语学习者看完视频后没法确认自己是否真的掌握了动作传统考试型练习又太生硬。所以我设计了一个练习中心把视频学习、选择题巩固、动作模仿打分、错题本复习串在一起。整个 App 采用 Flutter 跨端方案同时适配 OpenHarmony 和 Android练习中心作为独立模块开发方便后续迁移到其他技能学习场景。如果你正在做 Flutter 跨端应用或者打算接触 OpenHarmony 开发又或者想了解一个非标准学习类 App 的练习模块怎么设计这篇文章应该能给你一些可落地的参考。1. 项目整体设计与技术选型思路1.1 手语学习App的本质需求拆解手语学习和学英语、学乐器本质上是同一类需求动作记忆 肌肉记忆 定时复习。所以产品功能不能只有视频播放器必须包含记忆曲线干预和反馈机制。我把核心需求拆成三个层次基础层手语词汇的分类浏览和视频演示练习层基于题库的选择题、动作模仿、拼写题巩固层错题本、每日打卡、学习进度统计练习中心的定位就是在练习层和巩固层承担枢纽角色。用户每学完一个分类的手语词汇就要进入练习中心完成一组测试。测试结果决定哪些词进入错题本进而影响后续复习计划。1.2 为什么是 Flutter for OpenHarmony选型时我对比过三种方案ArkUI 原生开发、uni-app 适配、Flutter for OpenHarmony。ArkUI 原生性能没问题但只服务鸿蒙生态后续如果要出 iOS 版等于整套重写。uni-app 生态虽然成熟但复杂动画和手势识别这类重交互场景性能表现一直不太稳定。最后定 Flutter for OpenHarmony理由有三点Flutter 的渲染引擎是自绘的不依赖系统控件在 OpenHarmony 上的渲染一致性和 Android/iOS 几乎无差别Flutter 项目可以一套代码同时构建 OpenHarmony 和 Android 包对资源有限的小团队非常友好OpenHarmony 生态正在快速发展现在投入 Flutter 适配等生态成熟时团队已经有积累不过也要说实话Flutter for OpenHarmony 目前还不是官方主推路径插件生态比 Android/iOS 少一些部分原生能力需要自己写 MethodChannel 桥接这个后面会详细说。1.3 项目结构规划项目采用 feature-first 的目录结构确保练习中心可以独立演进lib/ ├── core/ # 基础能力网络、存储、主题、工具类 ├── features/ │ ├── home/ # 首页分类浏览、连续打卡入口 │ ├── learn/ # 学习模块视频播放、词汇详情 │ ├── practice/ # 练习中心答题流程、结果统计 │ └── profile/ # 个人中心错题本、学习报告 ├── models/ # 全局数据模型 ├── services/ # 业务服务题库服务、进度服务 └── main.dartpractice 模块内部再细分models、providers、screens、widgets。练习中心是功能独立度最高的模块接口只通过 repository 层对外暴露这样做的好处是以后做英语单词练习、乐器练习可以整体复用。2. 环境搭建与工程创建OpenHarmony适配2.1 Flutter for OpenHarmony 的 SDK 准备搭建环境是坑最多的环节。Flutter for OpenHarmony 不能直接用 Flutter 官方 SDK需要从 OpenHarmony SIG 维护的仓库拉取配置方式建议这样操作拉取定制版 Flutter SDK建议指定分支不要直接拉默认主分支因为默认分支可能处于不稳定状态拉取定制版 Flutter Engine这个通常不需要手动编译SDK 里会带预编译产物安装 DevEco Studio 作为 OpenHarmony 工程的 IDE并配置好 HarmonyOS SDK / OpenHarmony SDK 路径建议使用 FVM 管理 Flutter 版本因为 Flutter for OpenHarmony 分支版本和官方版本可能不同多项目并行时很容易混乱环境变量的配置是另一个关键点。需要让 Flutter 命令找到 OpenHarmony 的 SDK 路径并配置 Java 环境。我整理过一张对照表配置项建议值说明FLUTTER_GIT_URL指向 OpenHarmony SIG 的 flutter_flutter 仓库拉取定制版 Flutter SDKOHOS_SDK_HOMEDevEco Studio 中配置的 SDK 路径构建 OpenHarmony 应用时需要JAVA_HOMEJDK 17 或 DevEco Studio 内置版本版本不匹配会直接构建失败Flutter 分支选择带ohos后缀的稳定分支如3.7.22-ohos、3.10.5-ohos2.2 创建并配置跨端工程环境配置好后创建项目和官方 Flutter 基本一致flutter create --org com.example sign_language_app但和官方 Flutter 工程不同的是Flutter for OpenHarmony 的项目目录下不会有ios和android目录取而代之的是ohos目录。如果后续还需要支持 Android只要在同一个工程里执行flutter create --platforms android .把平台目录补上即可。工程创建后需要到ohos目录里检查build-profile.json5确认签名配置和 SDK 版本是否和本机一致。Debug 包一般会走自动签名但 Release 包必须到 AppGallery ConnectAGC上配置签名证书这一步绕不开。2.3 真机运行与调试配置OpenHarmony 真机调试和 Android 略有不同。确认hdc命令可用后执行hdc list targets flutter devices flutter run -d device-id如果flutter devices没有识别出设备多半是端口没配对执行hdc tconn 设备IP:5555重连一次就可以了。这里有一个容易踩的坑OpenHarmony 设备上的 HTTP 明文请求默认也是受限的如果后端的视频资源走 HTTP 协议需要在ohos目录的配置文件里声明网络权限和明文传输权限否则视频加载不出来你会误以为是播放器代码写错了。3. 手语学习核心功能的实现3.1 手语视频播放与学习模块设计手语学习模块的核心是视频播放体验。手语动作往往就在毫厘之间手型和运动轨迹差一点表达的意思就完全不同。所以播放器不能只是能放就行我做了三个针对性优化支持 0.5x 到 0.75x 的慢速播放让学习者看清手指动作的连贯性播放进度条支持上一帧/下一帧微调这是为了精确回看动作关键帧循环播放同一个视频直到用户主动进入下一环节播放器选型上Flutter 官方的video_player是新架构插件Pigeon 桥接在 OpenHarmony 上表现一般。我调研了一圈发现 OpenHarmony SIG 提供了video_player_ohos的实现针对鸿蒙媒体引擎做了适配。切换方式非常简单在pubspec.yaml里iOS 和 Android 用官方包OpenHarmony 用video_player_ohos通过dependency_overrides处理。这个做法算是当前阶段最稳妥的兼容策略。3.2 手势识别的接入思路MediaPipe练习中心里最有挑战性的功能是动作模仿打分——用户对着摄像头比划一个手语动作App 判断对不对、标准不标准。这个功能我没有完全自己造轮子而是接入了 MediaPipe 的 Hand Landmark 方案。整体链路是通过 camera 插件实时获取摄像头帧将每帧图像传给 MediaPipe 的 hand landmark 模型输出 21 个手部关键点的坐标计算关键点之间的角度特征和标准动作模板做相似度比对返回相似度分数作为用户动作是否达标的依据MediaPipe 在 Android 上有现成的mediapipe_flutter包但 OpenHarmony 上没有官方支持。我的做法是写一个 MethodChannel在 OpenHarmony 侧用原生代码加载 MediaPipe 的 C 推理库然后把关键点坐标返回给 Dart 层。这个方案实现成本不算高因为 MediaPipe 本身有纯 C 的部署方式。和标准动作比对时要注意不要直接比较所有关键点的绝对坐标因为每个人手型大小、摄像头角度不同绝对坐标完全没有参考价值。我最终用的是相邻关键点之间的角度列表这样既对缩放不敏感也避免了大角度倾斜带来的误差。3.3 学习进度与记忆曲线学习模块如果没有进度跟踪用户很容易学着学着就放弃了。我在后端设计了一个轻量级的进度模型核心是记录每个词汇的学习状态。状态分为四个等级not_started从未看过视频learning看过视频但没完成练习mastered练习正确率大于等于 80%reviewing曾经掌握但间隔超过 7 天没有复习状态机的变化规则是学习视频 - learning通过练习 - mastered超过间隔未复习 - reviewing。这个模型虽然简单但足够支撑记忆曲线的复习策略。每次进入练习中心系统会优先抽取reviewing状态的词汇因为它们正处于遗忘临界点。4. 练习中心从需求到实现4.1 练习中心的题目模型与题库设计练习中心的题目类型不搞花架子我定了三类选择题播放手语视频选对应的中文含义四选一反向题展示中文词汇选正确的手语动作视频模仿题用户对着摄像头按视频做动作AI 打分题库数据用 JSON 文件随包发布结构如下{ id: q_001, type: choice, word: 谢谢, videoUrl: assets/videos/thanks.mp4, options: [谢谢, 对不起, 你好, 再见], correctIndex: 0, category: greeting, difficulty: 1 }题目数据的加载我封装在PracticeRepository里通过loadPracticeSet(category)按分类拉取一组题目。每组题的数量设计为 10 道题型混合比例是 5 道选择题、3 道反向题、2 道模仿题。这个比例是结合用户测试反馈定的全选择题太无聊全模仿题太累混合起来练习意愿更高。4.2 答题流程与状态管理答题流程是整个练习中心的骨架。我画了一个清晰的状态流转加载中 - 答题中 - 结果反馈 - 下一题 - 全部完成。状态管理没有引入太重的东西直接用了Provider。原因很简单练习中心的状态量不多核心就是当前题号、得分、倒计时、答题状态Provider足够了。Riverpod是好但为了一个模块引入它会带来额外的学习和迁移成本收益不大。核心的练习状态模型enum QuestionStatus { loading, answering, answered, timeout } class PracticeState { final int currentIndex; final int totalCount; final int score; final int streak; final QuestionStatus status; final Duration remainingTime; }答题中的倒计时用Timer.periodic实现每道题给 15 秒。这里有个细节倒计时到 0 时要先取消 Timer再执行超时逻辑否则界面已经切到下一题了Timer 还在跑状态就会错乱。我在这上面踩过坑大家写代码时务必把 Timer 的生命周期管好。4.3 练习结果统计与错题本答完一组练习后结果页展示三块内容得分、正确率、错题列表。得分不是简单的对了 8 题就是 80 分而是加入了时间权重。15 秒内作答且正确得 10 分每题基础 8 分加上最快作答者的剩余时间加成这样能鼓励快速反应而不是慢慢吞吞看半天才选。错题本本质上就是一个持久化的题目 ID 列表。我用了shared_preferences_ohos这个适配鸿蒙的存储插件来存 JSON 字符串数据结构如下{ wrongQuestions: { q_001: { wrongCount: 3, lastWrongTime: 2025-01-20T10:30:00 } } }为什么不用数据库原因很简单错题量级撑死几百条JSON 序列化完全够用。引入数据库光是初始化、建表、升级的逻辑就够写几百行纯属给自己找事。错题本页面的设计我也做了点心思——每道错题都显示错误次数和上次错误时间错误次数超过 3 次的题目标记为易错在复习时优先出现。这样用户能看到自己的薄弱点练习中心也真正做到了数据驱动复习。4.4 连续打卡与激励体系练习中心如果只是干巴巴地刷题留存率会非常差。我参考了语言学习类 App 的通行做法加了连续打卡机制。用户每天完成至少一组练习就算打卡成功。打卡数据同样存到本地{ lastCheckInDate: 2025-01-20, streakDays: 12, totalCheckIns: 36 }这里有个小技巧判断是否连续打卡时不要简单比较日期字符串。我踩过一次坑——用户跨时区旅行后日期会中断导致打卡记录意外清空。改成先把日期转成时间戳按本地时区取零点再做差值计算就稳定多了。首页放了一个打卡日历组件已打卡的日期点亮一个标记连续打卡天数直接展示在显眼位置。连续天数超过用户预期时用户会有一种不想断掉的心理这是激励体系的核心作用。5. 常见问题排查与优化实录5.1 Windows下构建报错unable to find suitable Visual Studio toolchain在 Windows 上构建 Flutter for OpenHarmony 工程时经常会遇到 unable to find suitable Visual Studio toolchain 的报错。这其实不是 Flutter for OpenHarmony 本身的问题而是因为 Flutter 在 Windows 上构建原生 Windows 插件或者某些涉及原生代码编译的场景需要 Visual Studio 的 C 桌面开发组件。解决方案是在 Visual Studio Installer 里勾选使用 C 的桌面开发工作负载并安装 Windows 10/11 SDK。装完重启后再执行flutter doctor确认 Visual Studio 工具链变为绿色即可。提示如果公司电脑装 Visual Studio 需要管理员权限可以先联系 IT 开通。不要想着跳过这步Flutter 原生插件编译绕不开 C 工具链。5.2 OpenHarmony 画面渲染异常项目早期在 OpenHarmony 真机上运行时遇到过画面渲染异常部分页面闪烁、转场动画卡顿、甚至出现黑色块。排查了很长时间最后发现是 Flutter for OpenHarmony 在 GPU 加速和软件渲染之间切换不完善导致的。我的处理方法分两步在entry模块的配置中关闭部分场景的 GPU 加速强制走软件渲染稳定性优先将复杂的转场动画拆成多个轻量动画避免单帧计算量过载另外一个隐蔽的坑是图片资源过大。OpenHarmony 设备型号众多低端设备的 GPU 能力有限一张几 MB 的大图直接Image.asset加载内存压力和渲染压力都很大。统一加上图片压缩、缓存策略后渲染异常明显减少。5.3 Gradle 插件应用方式报错构建 Android 包时Flutter 项目报错 you are applying flutters main gradle plugin imperatively using the apply script。这是因为新版 Flutter Gradle 插件要求使用声明式的方式而不是传统的apply方式。解决办法是修改android/settings.gradle把apply from: $flutterRoot/packages/flutter_tools/gradle/app_plugin_loader.gradle改成声明式插件 DSLplugins { id dev.flutter.flutter-plugin-loader version 1.0.0 // 其他插件 }这种问题在 Flutter 版本升级后特别常见遇到不要慌定位到 Gradle 配置按新规范修改就行。5.4 Flutter 内存优化与 isolate 使用手语 App 和普通学习 App 最不同的地方在于视频资源密度很高。视频播放本身就是一个内存大户如果再加上 MediaPipe 推理内存压力会成倍增长。我重点做了三块优化第一视频内存控制。在video_player_ohos里设置合理的分辨率不要直接播放 1080p 源视频。手语动作的细节主要在手上720p 完全足够内存占用却可以下降 40% 以上。第二图片资源缓存策略。练习中心的封面图和选项图用cached_network_image做磁盘缓存同时限制内存缓存层的大小。第三MediaPipe 推理放到独立 isolate。MediaPipe 推理是 CPU/GPU 密集型的耗时操作如果直接在 UI isolate 里跑界面会卡顿到没法看。我把推理过程包在Isolate.run()里计算结果通过消息返回 UI isolate。实测下来UI 帧率从掉到 20 帧不到恢复到稳定 60 帧。final landmarkResult await Isolate.run(() { // 在后台 isolate 执行手部关键点推理 return mediaPipeHelper.processFrame(cameraFrame); });这里要注意传入Isolate.run的数据必须是可以跨 isolate 传输的相机帧数据要转成二进制格式不能直接传Uint8List的视图对象否则会报类型错误。5.5 网络请求封装与抓包技巧练习中心需要从服务器拉取题库更新和视频资源列表。网络层我基于 Dio 做了统一封装核心关注点有三个拦截器统一打印日志、超时时间按接口区分、错误码统一转成业务异常。抓包调试时也遇到了坑。默认情况下Dart 原生 HTTP 不走系统代理所以 Charles 抓不到 Flutter 的包。我用了一个取巧的办法在 Dio 初始化时手动设置代理地址。(dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate (client) { client.findProxy (_) { return PROXY 192.168.1.100:8888; }; return client; };这样虽然只适用于开发调试但比折腾系统级 CA 证书要省事。上线前记得把这段代码删掉不然用户设备上所有请求都会尝试连你开发机的代理接口会全挂。写在最后这个项目从搭建 Flutter for OpenHarmony 环境到实现完整的手语学习流程和练习中心前后花了两个多月。最大的感受是OpenHarmony 生态虽然没有 Android/iOS 那么成熟但 Flutter 这套跨端方案确实把开发门槛降到了可接受的范围内。遇到插件不兼容的问题先查 OpenHarmony SIG 的仓库有没有对应适配没有的话就自己写个 MethodChannel 桥接基本都能解决。练习中心这个模块的设计思路后续还可以继续往两个方向扩展一是引入更细粒度的动作评分报告指出用户的手型具体偏在哪里二是把练习题库改成云端动态下发配合运营做每日推荐。如果你也在做类似的技能学习类 App欢迎交流练习模块的经验。
返回列表