SMARTWATCH 学习手册有 C 基础 · 嵌入式从零开始
支线三 / Dart 与 Flutter App / 06

自己的 App 怎样真正连上手表

先让调试工具证明设备链路可用,再在程序中完成相同操作。每一步都有可观察的状态,最后才把通知交给前一篇的解析流程。

接续上一篇:收到七个字节,怎样显示成 4000 毫伏

1 · 先确认平台和依赖版本

实际蓝牙实验使用 Android 真机,并先按主线无线篇用调试工具连接手表。这样扫描不到时,能区分设备广播、系统权限和 App 代码问题。SDK 是工具包,flutter_blue_plus 是让 Dart 调用系统蓝牙能力的插件;两者不是同一个东西。

仓库 pubspec.lock 锁定插件 1.36.8。练习优先按此版本 API 阅读;pubspec.yaml 中的版本范围不代表每次都选到同一版本。Android 不同版本的扫描、连接权限要求不同,先按插件该版本说明和平台文档配置 AndroidManifest,再在运行时处理授权拒绝及蓝牙关闭。

2 · 把“连接”拆成五个可见步骤

连接已建立与已能交换完整消息是不同阶段。
连接已建立与已能交换完整消息是不同阶段。 · 教学示意

在扫描等待期间显示“扫描中”,扫描失败和无结果分别提示。连接以后还需要服务发现和通知订阅,缺少目标特征不能当作整个功能已经就绪。仓库连接状态事件在服务发现之前就可能变为 true,页面不能只凭这个布尔值判断全部通信条件。

3 · 沿着已有接口实现一次操作

学习调用顺序 · 实际页面应显示列表,由用户选择设备

final ble = SmartWatchBLE();

final devices = await ble.scan();
if (devices.isNotEmpty) {
  final connected = await ble.connect(devices.first.device);
  print("connection result: $connected");
}

SmartWatchBLE 已在仓库实现。scan 返回扫描结果列表,connect 接收设备对象;本片段只说明接口衔接,页面不应未经选择就总连第一台。把调用放在一次按钮操作中,用 try/finally 清理忙状态,并处理没有设备的情况。

State 中登记订阅;对象与订阅都要在明确的位置释放

final batterySub = ble.onBattery.listen((battery) {
  if (!mounted) return;
  setState(() => millivolts = battery.mv);
});

onBattery 是解析后的电量事件流,battery.mv 是毫伏字段。此时组件只处理已解释的数据,不在 build 中扫描,也不在每个 Text 内部重新做协议解析。

4 · 断开后,哪些状态还需要处理

主动断开、设备离开和系统关闭蓝牙,都可能结束连接。旧通知订阅应取消,接收缓冲应清理,不完整备份也应停止。再次连接时重新发现服务、找到特征并订阅,不能沿用旧连接中的特征对象。

页面退出时释放自己持有的订阅。当前 DashboardPage 还会调用服务的 dispose;如果未来把服务改成跨页面共享,就要重新确定由谁释放,不能几个页面互相关掉同一个对象。先用现有的单一页面持有关系读清楚。

5 · 把失败也作为实验结果

分别观察蓝牙关闭、拒绝权限、扫描无结果、找到设备但缺少特征、连上后断开这几条路径。日志至少要显示停在哪个步骤,避免所有错误只显示“连接失败”。

动手观察

先接收一条真实电量帧,再返回列表重新连接两次,确认没有重复显示同一通知。记录手机状态、原始通知和解析结果,主控端同时核对串口收发。

资料与代码