说是“答题结果实现”,其实背后牵扯到的内容不少:答题状态怎么管理、结果怎么算、页面怎么跳转时把数据带过去、得分和等级怎么可视化,还有最关键的——在 OpenHarmony 真机上跑起来之后,Flutter 那套热重载和插件机制到底能信几分。我这次用 RK3568 开发板做了完整实测,过程中踩了不少坑,有些是 Flutter 的通用问题,有些是 OpenHarmony 平台特有的。这篇文章就把答题结果从数据到 UI 的完整链路拆开讲清楚,顺带把真机调试时遇到的设备树和插件版本问题一并记录,给打算在 OpenHarmony 上做 Flutter 应用的同行省点时间。
1. 为什么是 Flutter + OpenHarmony,而不是再来一套原生
先聊一个经常被问到的问题:垃圾分类指南这种 App,功能不算复杂,页面也就那么几个,为什么非要用 Flutter 适配 OpenHarmony,而不是直接用 ArkUI 写原生?
我的判断依据很直接:团队已经有一套 Flutter 代码库承接 Android 和 iOS 业务,OpenHarmony 要做的就是把这套资产平移过去,而不是从零重写。 垃圾分类指南这个 App 的核心交互是列表浏览、搜索、答题和结果展示,不涉及大量平台私有能力,Flutter 的跨端渲染在这类场景下损耗极小。
OpenHarmony 对 Flutter 的支持走的是 OpenHarmony SIG 维护的 flutter_flutter 仓库,相当于 Flutter 官方框架在 OpenHarmony 平台上的移植分支。开发时你写的还是标准 Dart 代码,UI 组件库也还是 Flutter 那套 Widget,只不过最终构建产物从 Android 的 APK 变成了 OpenHarmony 的 HAP。也就是说,前端业务代码这一层,几乎可以做到不改逻辑、只改工程配置。
从我实际的移植体验来看,业务层代码确实能大批量复用,但工程层需要单独处理三件事:
pubspec.yaml依赖要核对一遍,不是所有 Flutter 插件都在 OpenHarmony 上实现了原生通道;- 图片、字体等资源路径和打包方式保持 Flutter 惯例,但部分原生插件需要替换为社区适配版本;
- 构建工具链从 Gradle 切换到 hvigor,工程结构变化比较大。
所以在项目启动前,我做了一次依赖体检。垃圾分类 App 的依赖很少,核心就是 provider 做状态管理、shared_preferences 做本地存储、dio 做题库接口请求,这三个在 OpenHarmony 上都有对应适配。确认之后,才敢放心排期。
另外一个原因和分发渠道有关。OpenHarmony 的设备形态很多,从开发板到 IPC、从电视到教育平板,操作系统版本碎片化程度比手机更夸张。用 Flutter 做跨端统一,至少在 UI 一致性和渲染性能上不用每个设备单独调,省掉的都是真金白银的工时。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 答题模块的数据模型与判定策略
垃圾分类指南里的答题功能,不是简单的选择题对错,而是希望用户答完题之后,能够看到自己对于垃圾分类知识的掌握情况,并给出有针对性的学习建议。这就要求答题结果不能只是一个分数,还得有分类维度的分析。所以我在设计数据模型的时候,把题目标签和解析都做了结构化。
2.1 题目模型设计:答案、标签、解析三件套
题库里的每题数据,我设计了下面这个模型:
dart复制class QuestionModel {
final String id;
final String question;
final List<String> options;
final int correctIndex; // 正确答案在 options 中的下标
final String analysis; // 答案解析
final String category; // 垃圾类别标签:可回收、有害、厨余、其他
final int difficulty; // 难度系数:1-3
const QuestionModel({
required this.id,
required this.question,
required this.options,
required this.correctIndex,
required this.analysis,
required this.category,
required this.difficulty,
});
}
category 这个字段是垃圾分类领域特有的,因为最终结果页要把得分拆解到四个垃圾类别上,告诉用户到底是对"可回收物"不熟,还是对"有害垃圾"掌握得差。没有这个标签,结果页就只能给一个干巴巴的分数,价值会大打折扣。
关于 correctIndex 有一个容易忽视的点:题库接口返回的选项顺序,不应该和数据库存储顺序完全一致。尤其像垃圾分类这种题目,选项普遍是"香蕉皮、报纸、电池、过期药品"这类具体物品,如果每次刷到的顺序都相同,用户会产生位置记忆而非知识记忆。我在加载题库的时候,会做一次随机洗牌,同时同步洗 correctIndex 的指向。这一点在小程序问答里很常见,但自己做的时候非常容易忘。
2.2 判定策略:为什么我选了交卷统一判定
答题界的判定策略可以分两种:选完即判,或者交卷统一判。
垃圾分类场景,我的选择是交卷统一判定。原因有两点:
第一,题目设计里有一部分是多选题的变体,比如"下列哪些属于有害垃圾"。选完即判的多选逻辑很绕——你是每选中一个选项就校验一次,还是等用户点下一题时再判断?处理成本高且交互不自然。
第二,用户做分类练习的时候,往往会边答边犹豫,尤其是拿不准的题目,会反复切换选项。选完即判会给出明确的视觉反馈,表面上看是"即时纠错",实际上会打断用户的思考节奏,让练习变成对答案而非对知识的回顾。
所以流程上我采用了经典的三段式:作答阶段只记录用户选项,不判断对错;点击交卷后统一遍历答案列表计算得分;计算完得分再通过路由携带参数跳转结果页。
2.3 答题明细记录:结果页的数据来源
用户交卷后,我需要把每一题的作答情况打包传递到结果页。这里我定义了答题记录模型,它同时服务于分数计算和结果页的逐题解析:
dart复制class AnswerRecord {
final QuestionModel question;
final int selectedIndex; // -1 表示未作答
final bool isCorrect;
AnswerRecord({
required this.question,
required this.selectedIndex,
}) : isCorrect
