最近在做一个基于Flutter和开源鸿蒙的智能居家康养助手,说白了就是把老人居家场景里的智能设备统一管起来:床垫监测、血压计、室内环境传感器、智能药盒、呼叫按钮这些,做成一个手机端和Pad端都能用的App。项目里踩了不少坑,也沉淀了一些实操经验,特别是列表交互优化、详情页和设备控制落地、以及多终端适配这块,今天完整复盘一下。
如果你正在做Flutter跨端应用,或者开始接触OpenHarmony生态,又或者做IoT类App被列表卡顿、设备控制状态同步这类问题折磨过,这篇内容应该能帮你少走不少弯路。我会把从架构设计到具体实现、再到调优和排坑的完整过程都写出来,代码和方案都是实测过的。
1. 项目整体设计与技术选型思路
先交代一下背景。这个项目的目标终端并不只有手机,还有挂在墙上的控制平板、客厅电视端、甚至床头柜上的小型智能屏。设备系统也杂,既有开源鸿蒙OpenHarmony的,也有存量Android设备。这种多系统、多终端的局面,选择跨端框架基本是必然的。
1.1 为什么选Flutter配合开源鸿蒙
当初选型时对比过React Native、原生双端开发,最后定的是Flutter,原因很实际:
第一,Flutter自绘引擎的跨端一致性确实强。康养场景里UI有大量卡片、图表、大字体信息展示,用Flutter能保证在手机、平板、电视上渲染效果一致,不会出现同一套布局在安卓上正常、在鸿蒙上却错位的尴尬。
第二,开源鸿蒙生态这两年起来了,对Flutter的适配也越来越成熟。OpenHarmony官方社区有Flutter的适配分支,常见的Widget和Plugin都能跑起来。这意味着写一套Flutter代码,可以同时覆盖Android和OpenHarmony设备,省下的维护成本非常可观。
第三,Dart语言的强类型特性和Flutter的声明式UI,让IoT设备状态管理变得相对可控。后面会详细讲设备控制的状态同步,这方面Flutter的Stream和状态管理方案比原生手写回调要清晰得多。
选型时也有过担心:Flutter在低端设备上的性能、鸿蒙平台对Flutter插件的兼容度。实际跑下来,中低端设备上Flutter的渲染性能依然够用,前提是做好列表优化和build方法瘦身,这个后面展开。
注意:我是从2024年初开始接触开源鸿蒙上的Flutter适配,到写这篇文章时,常见的UI场景和大部分业务插件都已经能稳定运行。但如果你要接蓝牙、NFC这类底层能力,最好先查一下目标设备的系统版本和适配分支支持情况,再决定是直接调Flutter插件,还是走鸿蒙原生插件通过MethodChannel桥接。
1.2 整体架构与模块划分
项目架构我采用的是层状结构加模块化,没有用很重的微前端或者插件化方案,原因很简单:团队规模不大,架构太重反而拖慢迭代速度。
核心分四层:
- UI层:所有页面和组件,负责渲染和用户交互
- 状态管理层:使用Provider + ChangeNotifier,管理页面状态和设备状态
- 业务逻辑层:封装设备控制、数据上报、告警判断等业务规则
- 数据与服务层:封装HTTP、WebSocket、本地存储,屏蔽底层通信细节
用这种分层主要是考虑到康养场景的特殊性:设备数据实时性要求高,需要长连接推送;告警逻辑(心率异常、跌倒、久未活动)要快速响应;而且多终端登录时状态要保持同步。分层清晰了,这些能力都可以在业务逻辑层统一处理,不会散落在各个页面里。
模块划分上,按功能域拆成:设备管理、健康数据、告警中心、用户与家庭、设置。每个模块内部再分包,模块间通过统一的Service层通信,尽量避免页面直接互相调用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 列表交互优化:从能用到好用
列表是这类应用的门面:设备列表、健康记录列表、告警消息列表,用户每天打开看到最多的就是它们。但列表做得不好,轻则卡顿掉帧,重则直接白屏崩溃。这个项目里列表相关的问题我前前后后优化了三轮,说几个最关键的。
2.1 列表卡顿的5个元凶与排查方法
第一轮优化前,设备列表滚动掉帧非常明显。我的排查方法是用Flutter自带的Performance Overlay和DevTools的Timeline逐帧分析,定位到5个问题:
- build方法里做了太多事。列表项的build里直接调用了设备状态解析、JSON格式化、甚至图片裁剪,每次滚动都重复执行一遍,不卡才怪。
- 没有用itemExtent或prototypeItem。ListView每个item高度都需要动态计算时,滚动就会反复触发layout,性能损耗很大。
- 图片没有缓存或不规范。设备缩略图直接加载网络原图,没有缩略图,自定义缓存策略也粗糙,快速滑动时图片解码卡顿严重。
- setState粒度太粗。设备状态变化时直接刷新整个列表页面,导致所有可见item全部重建,而不是只刷新状态变化的那个。
- 大量Stream没有取消订阅。页面销毁后,设备状态流还在回调,导致在后台继续重建widget。
针对这些问题,我当时写了个列表优化checklist,后面做类似需求时都直接照着排查:
- DevTools里看build和layout的耗时占比,build占比高说明build方法里有重活
- 用debugProfileBuildsEnabled开启build耗时统计,对比优化前后
- 检查列表有没有设置itemExtent或prototypeItem,没有的补上
- 检查item的图片有没有统一走缓存和缩略图
- 检查StatefulWidget的State对象是否存在内存泄漏
2.2 列表项拆分与复用细节
优化时我把列表项组件做得很轻量,尽量拆成无状态组件,只有状态变化的部分才用Consumer包裹。比如设备列表中,一个设备卡片需要展示:设备图标、名称、在线状态、当前状态值、告警角标。其中只有状态值和告警角标是高频变化的,我就只让这两个部分监听状态流。
操作上大概是这个思路:
dart复制class DeviceListItem extends StatelessWidget {
final Device device;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(12),
child: Row(
children: [
DeviceIcon(device.type),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(device.name, style: TextStyle(fontSize: 16)),
SizedBox(height: 4),
// 只有这里订阅状态变化
Consumer<DeviceState>(
builder: (context, state, _) => Text(
state.stateDesc,
style: TextStyle(color: state.isNormal ? Colors.green : Colors.red),
),
),
],
),
),
// 只有这里订阅告警角标变化
Consumer<AlertBadge>(
builder: (context, badge, _) => badge.visible ? Badge(count: badge.count) : SizedBox(),
),
],
),
);
}
}
这招最直观的效果就是:某个设备的电量从90%掉到89%,只有那一个item里的小小状态文本重建,整个列表其他item完全不受影响。实测列表帧率稳定在60fps。
2.3 下拉刷新、加载更多与骨架屏的实操
列表交互除了滚动流畅,使用体验也很重要。下拉刷新、加载更多、骨架屏这三件套我都有落地,说几个容易忽略的细节。
下拉刷新我用的 RefreshIndicator,需要注意 onRefresh 必须返回 Future,如果刷新逻辑是同步的,要包一层 Future.delayed,否则刷新指示器会闪一下立刻消失,交互体验很差。另外刷新时要保留当前滚动位置,不要刷新完直接跳回顶部——用户可能正在看列表中间的某条数据,结果刷新后位置丢了,这很烦人。做法是记录刷新前的 firstVisibleIndex,数据更新后通过 ScrollController 跳回去。
加载更多用的是 ScrollController 监听滚动到底部附近时触发加载,阈值设为距底部200像素。这个值的设定考虑是:太早会频繁触发加载,太晚会在快速滚动时看到列表底部空白等待。触发后要处理好“正在加载更多”的状态,防止重复触发,同时要显示footer加载指示器。
骨架屏在列表初次加载时非常有用,尤其康养场景里用户往往是中老年人,看到空白屏会觉得App坏了。我设计了一个 DeviceListSkeleton,模仿设备卡片的布局画几个灰色的圆角和线条块,数据到达后切换回真实列表,切换时加一个淡入淡出动画。视觉上很自然,也给用户一个“正在加载”的心理预期。
2.4 左滑操作与点击反馈的细节设计
列表项的交互细节也直接影响使用感受。设备列表里,每项都支持左滑露出“查看详情”和“解绑设备”两个操作。这里有几个实战经验:
左滑操作我用 Dismissible 配合 confirmDismiss 实现,但注意 Dismissible 在 Android 和鸿蒙上的滑动方向感差异,左右滑动方向要根据用户习惯配置,不要默认往左滑出操作再往左滑删除,容易误触。我最终采用了“左滑露出固定操作按钮”的方案,而不是 Dismissible,因为设备解绑这种危险操作不适合滑动即删,误触风险太高。
点击反馈要区分“点了之后有后续页面”和“点了只是切换状态”两种情况。前者要有水波纹效果,后者比如开关按钮,要用 Switch 动画让用户清楚看到状态切换。还有个细节:设备卡片点击区域要足够大,建议高度不小于48像素,这是无障碍设计的基本要求,对中老年用户尤其重要。
3. 详情页与设备控制落地
列表解决的是“看到设备”,详情页和控制解决的才是“用设备”。康养设备控制逻辑看起来简单,无非是下发指令、改变状态,但落地时涉及的链路比想象中长得多。我把这块拆成三部分讲:详情页信息架构、控制指令链路设计、以及状态同步与异常处理。
3.1 详情页信息架构设计
详情页我设计了三个主要区域:顶部设备状态概览、中部实时数据卡片、底部控制区。
顶部设备状态概览的视觉要足够醒目。设备是否在线、电量、网络信号、当前工作状态,这几项信息要在第一眼就能看到。对康养设备来说,“设备离线”是最需要用户注意的状态,我用一个大大的红色离线标识加文字提示,而不是藏在角落里的小图标——中老年用户的视力普遍下降,这种关键信息必须大而醒目。
中部实时数据卡片是详情页的核心内容区。不同类型的设备展示不同的数据卡片:
- 智能床垫:睡眠状态、心率、呼吸频率、体动次数
- 血压计:收缩压、舒张压、心率、测量时间
- 室内环境传感器:温度、湿度、PM2.5、甲醛浓度
- 智能药盒:今日用药计划、已服/未服状态、下次用药时间
每个数据卡片都支持点击展开查看趋势图。趋势图我用 fl_chart 这个库绘制,支持折线图。注意图表数据不要一次性加载全部历史数据,先用最近1小时的数据渲染,再根据用户选择的时间范围按需加载。
底部控制区根据设备类型动态渲染。像智能床垫这种设备,控制项有:调整头部抬升角度、开启/关闭离床监测、设置翻身提醒频率。像智能药盒,控制项有:设置用药时间、确认已服药、暂停用药计划。控制区用卡片式布局,按钮大、间距大,文字说明清楚,方便中老年用户操作。
3.2 设备控制指令链路设计
设备控制最核心的是指令链路。我采用的是“App -> 云端 -> 设备”的标准IoT链路,App不直连设备,而是通过云平台下发指令。这么设计的原因后面讲清,先看具体流程。
链路分五步:
- 用户在详情页点击控制按钮,比如“调整床垫头部抬升到30度”
- App生成一条控制指令,包含设备ID、动作、目标参数、请求ID
- 指令通过WebSocket发送到云端指令通道
- 云端把指令转发给设备,设备执行后上报执行结果
- 云端把结果推回App,App更新UI状态
这条链路的每一步都需要做好异常处理。我在代码里加了一个请求超时机制:指令发送后,如果10秒内没有收到设备执行成功的回调,就判定为控制失败,提示用户检查设备状态。这个超时时间是根据实际设备响应速度反复测试出来的,太短会导致误报失败,太长会让用户觉得App卡死了。
指令协议我设计成JSON格式,结构大概是这样的:
json复制{
"cmd": "device.control",
"requestId": "req_1234567890",
"deviceId": "bed_001",
"action": "adjust_head_angle",
"target": 30,
"timestamp": 1716000000000
}
每个字段都有明确作用:requestId 用于关联请求和响应,排查问题时特别有用;timestamp 用于服务端校验指令是否过期,防止旧的指令在设备离线重连后突然执行,这个坑后面细说。
3.3 设备控制的状态管理与UI同步
设备控制完成后,UI必须同步更新。这里我踩过的坑是:设备执行完指令后的状态上报,和我本地预测的状态,不是一回事。
举个例子,用户点击“关灯”,App本地先设置了灯的状态为关闭,UI立刻更新——这是“乐观更新”。但如果设备执行失败了,或者设备上报的实际状态还是开着的,UI就显示错误了。康养场景里不能这么做,如果设备实际没关,但App显示关了,老人可能就放松警惕,后果不堪设想。
所以我做的是“保守更新”:用户点击控制按钮后,UI进入“指令已发送”状态,按钮变为不可用并显示加载动画,等待云端确认设备执行完成后,再更新为最终状态。这个策略损失了一点即时反馈的爽快感,但在康养场景里,正确性远重要于交互反馈的速度。
指令状态我用枚举类管理:
dart复制enum ControlCommandStatus {
idle, // 空闲
sending, // 指令发送中
sent, // 指令已送达
executing, // 设备执行中
success, // 执行成功
failed, // 执行失败
timeout, // 超时
}
UI层监听这个状态,不同状态显示不同的按钮效果和文字提示。比如 sending 状态显示“正在发送指令”,executing 状态显示“设备执行中”,failed 状态显示“控制失败,点击重试”。这样用户对整个控制链路的状态一目了然。
3.4 设备离线、异常与安全兜底
康养场景的设备控制不能只考虑“正常工作时”,更要考虑“不正常工作时”。我当时整理了几个典型的异常场景,每个都做了兜底:
- 设备离线:设备不在线时,控制按钮置灰,点击提示“设备已离线,请检查电源和网络”。同时列表页和详情页要明确展示离线状态,不能模棱两可。
- 指令超时:10秒未收到执行结果,弹出提示并提供重试入口。重试时用新的requestId,旧的requestId作废,避免重复执行。
- 设备执行失败:设备会返回失败原因,如“设备异常”“角度超限”,把这些原因透传给用户,同时记录日志方便排查。
- 重复指令:用户在等待结果时连续点击多次,要对相同指令做去重,防止设备收到重复指令。做法是在发送前检查当前设备的指令状态,如果还在执行中,忽略新的指令。
- 指令过期保护:设备离线时收到的指令缓存在云端,上线后才下发。这些指令如果是很久之前的,比如用户三天前点了调节温度,设备三天后才上线,不应执行这条指令。所以指令里带
timestamp,服务端和App都校验时间窗口,超过30分钟的指令直接丢弃。
这些兜底逻辑写起来不复杂,但每一行都是真实设备环境里可能踩到的坑。
4. 鸿蒙多终端全场景适配
标题里写了“多终端全场景适配”,这是我做这个项目最有切身体会的一部分。开源鸿蒙的定位就是多设备、全场景,同一套系统可以跑在手机、平板、电视、智能屏、手表上。但理想很丰满,实际做适配时头大得很。这里是我在适配过程中比较典型的几个问题。
4.1 屏幕适配与自定义组件的尺寸策略
先说最基础的屏幕适配。
Flutter本身有 MediaQuery.of(context).size 能拿到屏幕尺寸,但不同终端的宽高比差异太大:手机是20:9的长条,平板是4:3的方屏,电视是16:9的宽屏。如果直接用固定像素值布局,在电视上会显得元素过小,在手机上会显得很挤。
我的方案是引入“基准宽度”的设计思路:以手机屏幕宽度375为基准设计UI,然后根据实际屏幕宽度做等比缩放。具体实现上,写了一个 SizeHelper 工具类:
dart复制class SizeHelper {
static const double designWidth = 375;
static late double scaleFactor;
static void init(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
scaleFactor = screenWidth / designWidth;
}
static double sp(double size) => size * scaleFactor;
static double w(double width) => width * scaleFactor;
static double h(double height) => height * scaleFactor;
}
在 MaterialApp 的 builder 里初始化 SizeHelper,之后所有自定义组件的大小、间距、字号都用 SizeHelper.sp/w/h 来算,而不是写死像素值。这样同一套代码在手机、平板、电视上都能自动缩放。
但要注意,等比缩放不是万能的。电视这种大屏设备,用户坐得远,字体太小看不清就完蛋了。所以我还加了一层“最小字号保护”,所有关键文字用 MediaQuery.textScaler 处理,保证字号不小于某个安全值。
另外一个容易被忽略的点是安全区。手机有刘海屏、挖孔屏,平板有圆角,电视有过扫描。Flutter的 SafeArea 组件能自动避开系统安全区,但电视上要做特殊处理,四边都要留出足够的边距,防止UI被裁切。我做了一个 ScreenSafeContainer 组件,根据终端类型动态设置四边padding。
4.2 不同终端的组件交互差异
同样的交互,在不同终端上用户预期是不一样的,不能一套逻辑硬套。
手机上的操作逻辑是“点击 -> 跳转新页面”。列表每一项点击进去,是一个独立的详情页,通过Navigator.push压入路由栈。
但平板和电视上,页面空间足够大,继续用push全屏页就会显得很蠢。我改成“主从布局”:主内容区在左侧,点击列表项时在右侧的详情区域切换内容,而不是跳转新页面。这套逻辑用Flutter实现挺简单,无非是判断屏幕宽度决定用哪种布局,但设计思路要提前想清楚,后面改起来成本很高。
电视还有一个特殊问题:遥控器操作。电视没有触摸屏,用户只能用遥控器的上下左右键和OK键操作。这意味着所有交互都必须支持焦点导航,用 Focus 和 FocusScope 管理焦点的位置和样式。我当时给所有可交互组件都加了 focusColor 和 onFocusChange 的视觉反馈,这样在遥控器操作时能清楚看到当前聚焦的是哪个元素。
4.3 横竖屏切换与多终端断连重连
康养场景里,墙上的控制平板通常是横屏挂载,手机一般是竖屏使用。横竖屏切换导致的状态丢失问题必须处理。
我用的策略是:页面级别统一锁定方向,但支持在不同页面上设置不同方向。列表页和详情页允许横竖屏自由切换,但设备控制过程中锁定当前方向,防止操作到一半界面重排导致误触。做法是在设备控制指令发送前 SystemChrome.setPreferredOrientations 锁定方向,指令完成后再解锁。
横竖屏切换时如果页面要重建,状态管理要做好数据持久化。我当时用了 PageStorage 保存列表滚动位置和筛选条件,切换回来后恢复,避免用户看列表看到一半,旋转一下手机就回到列表顶部的尴尬。
多终端断连重连,这个问题在真实的IoT场景里非常常见。Wi-Fi波动、设备休眠、网络切换都可能导致App和设备之间的长连接断开。为此我在云端通道和App之间做了一套自动重连机制:
- 检测到WebSocket断开后,立即尝试重连,指数退避策略:第一次1秒后重连,第二次2秒,第三次4秒,最多30秒
- 重连成功后,App主动拉取所有设备的最新状态,确保UI上的设备状态不是过期的
- 重连期间,设备状态变化可能会产生推送,如果错过的推送,通过重新拉取补齐
这套机制做下来,多终端上的设备状态准确率能稳定在99%以上,用户体验不会因为网络波动而中断。
5. 常见问题与避坑记录
最后把我在这个项目里遇到过的几个高频问题整理成一个速查表,每个问题都写了现象、原因和解决方案,希望对正在做类似项目的朋友有帮助。这些问题有些是Flutter通用问题,有些是开源鸿蒙平台特有问题,都值得留意。
| 问题现象 | 原因分析 | 解决方案 |
|---|---|---|
Flutter编译报错 Error resolving plugin [id: 'dev.flutter.flutter-plugin-loader', version: ...] |
插件加载器版本与Flutter SDK版本不匹配 | 检查并升级Flutter SDK到插件要求的版本,同时清理Gradle缓存后重试 |
| 开源鸿蒙设备上运行Flutter应用,部分插件方法无响应 | 插件没有适配鸿蒙平台,或运行在兼容层上没有完整实现 | 查阅插件的鸿蒙适配状态,必要时用MethodChannel手动桥接原生能力 |
| 列表滚动掉帧 | item build方法过重、没有缓存图片、setState范围过大 | 瘦身build方法、引入itemExtent、拆分StatefulWidget到最小粒度、使用缩略图 |
| 设备控制后UI状态与实际设备状态不一致 | 采用了乐观更新,但设备实际执行失败 | 改为保守更新,等待设备确认执行结果后再更新UI |
| 横竖屏切换后列表位置丢失 | 页面重建导致状态丢失 | 使用PageStorage保存滚动位置和筛选条件 |
| 电视端遥控器无法操作 | 组件没有实现焦点导航 | 用Focus和FocusScope管理焦点,增加聚焦视觉效果 |
| App切后台再回来,设备状态显示离线 | 长连接被系统断开 | 实现自动重连机制,重连成功后主动拉取设备最新状态 |
| 底部弹窗内含TextField时,键盘弹起后弹窗被顶出视野 | 未处理键盘避让 | 使用isScrollControlled: true + Padding包裹,监听viewInsets调整高度 |
5.1 几个容易被忽略的细节
再说几个我在实际开发中总结出来的细节,都是代码Review时不容易发现、但运行起来很影响体验的:
底部弹窗里的TextField。详情页设置用药时间时会弹出一个底部弹窗,弹窗里有时间选择器和备注输入框。如果弹窗没有处理好键盘避让,键盘弹起来会直接遮挡输入框。解决办法是给 showModalBottomSheet 设置 isScrollControlled: true,然后用 AnimatedPadding 包裹内容,padding的底部值等于 MediaQuery.of(context).viewInsets.bottom,这样键盘弹起时弹窗会自动上移。这个细节不处理,用户输入文字根本看不见自己在敲什么。
PageStorageKey的使用。列表页面的每个item我都加了 PageStorageKey,这个key能在页面重建时保存item的滚动信息。但注意,key必须在列表里有唯一性,不能直接用index当key,否则列表重新排序时状态会错乱。我是用设备ID作为key:PageStorageKey(device.deviceId)。
设备状态值很长时的超长文本处理。智能床垫的睡眠状态描述有时候很长,比如“左侧卧、呼吸频率18次/分、当前处于浅睡眠阶段”。如果UI没有处理文本超长,会直接溢出报错。统一给这些文本加上 maxLines 和 overflow: TextOverflow.ellipsis,同时支持点击查看完整描述。
5.2 调优实践中的几个心得体会
这套项目做下来,我对Flutter与开源鸿蒙组合的稳定性和适用场景有了更实际的判断。这里分享几个心得,大家做类似项目时可以少想几天。
关于状态管理方案,这个项目用的是Provider,原因是简单直观、学习成本低、团队新成员上手快。项目里设备状态是高频变化的数据,Provider加ChangeNotifier在这个场景下完全够用。但如果你做的是复杂的多人协作编辑、大量并发状态,可能要上Bloc或者Riverpod,按项目复杂度选,不要盲目追新。
关于代码层面的性能优化,Flutter性能优化的思路和原生开发不一样。原生的卡顿优化重点是减少主线程任务,Flutter的重点是减少build次数和layout复杂度。所以写组件时我会尽量用 const 构造函数,能用 const 的地方绝不用普通构造函数,这样可以跳过一些不必要的重建。这已经是个习惯,不是刻意的优化了。
关于真机调试,开源鸿蒙的Flutter应用在模拟器上的表现和真机差距很大。特别是传感器数据、蓝牙通信这类能力,模拟器基本没法模拟。可以的话尽早拿真机调试,不要拖到开发后期才真机联调,否则会发现很多功能在模拟器上看着正常,一到真机就各种问题。
关于设备状态流,在Flutter里做设备状态的订阅发布,一定要控制好生命周期。State的initState里订阅、dispose里取消订阅,这个基本操作不能少。实际项目里我发现很多卡顿问题、内存泄漏问题的根源就是Stream没有被正确关闭,而不是什么高深的框架问题。
5.3 针对后续扩展的一点建议
这个项目基础的框架搭好了,后续扩展有几个方向,我先把思路写出来供大家参考:
告警推送集成。康养场景告警推送是刚需,老人心率异常、离床超过预定时间,都要第一时间推送给监护人。后续可以考虑接入厂商推送服务,但要注意鸿蒙和Android的推送通道机制不同,需要分别适配,不能一套代码走天下。
**语音交互。**墙上的控制平板和床头设备,对语音交互的需求非常强烈,老人不会操作复杂的UI,直接说“打开床头灯”“把床抬起来一点”会更自然。鸿蒙生态里本身有语音能力,Flutter端可以走系统级语音识别服务,用SoundRecorder录音、上传识别、返回结果再下发控制指令。
多设备联动场景。比如“离开床铺超过15分钟且室内温度低于18度”这种跨设备的联动告警,目前是单设备控制,后续可以在云端做简单的规则引擎,配置场景联动。App端只需要展示联动场景的状态和启停控制。
**离线边缘计算。**部分设备响应要求高的场景,比如紧急呼叫按钮,不能完全依赖云端链路。可以考虑在本地网关设备上做边缘计算,控制指令直接局域网下发,云端只做消息同步和数据存储。这样可以极大缩短指令延迟,同时即使云端不可用时,本地控制依然可用。
这些方向都是我在实际项目中逐步摸索出来的,有些已经验证可行,有些还在验证阶段。技术选型永远是为业务服务的,康养场景的特殊性决定了“稳定、可靠、易用”永远排在“炫酷、新潮”前面,这个原则贯穿了所有设计决策。
跑完这个项目,我最大的感受是:跨端开发的难点从来不在技术本身,而在对业务场景的深入理解。同样的Flutter框架,做电商App和做康养App,侧重点完全不一样。如果你也在做类似的项目,希望这篇博客能给你一些参考,少踩一些我踩过的坑。
