- BLE: 修复Omron设备Indication模式连接(先订阅后配CCCD + forceIndications) + 直连重连
- BLE: 设备断连实时检测(connectionState流→Provider→UI)
- 修复: 血压数据上报后端枚举不匹配(Device→DeviceSync)导致500
- 新增: DELETE /api/health-records/{id} 后端接口
- 设备管理页: 白色主题重设计 + 直连重连 + 实时状态 + Toast通知
- 设备扫描页: 白色主题 + 扫描动画居中 + 已连接等待页面
- 健康记录: 左滑删除(Dismissible) + 同时清理_allRecords和_filtered
- 导航: 修复自定义路由栈下Navigator.pop黑屏bug
- 文档: 医生端合并App完整设计文档(已确认版)
109 lines
3.1 KiB
Dart
109 lines
3.1 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'auth_provider.dart';
|
|
import 'data_providers.dart';
|
|
import '../models/bp_reading.dart';
|
|
import '../services/omron_ble_service.dart';
|
|
|
|
/// BLE 服务单例
|
|
final omronBleServiceProvider = Provider<OmronBleService>((ref) {
|
|
return OmronBleService();
|
|
});
|
|
|
|
/// 设备绑定状态
|
|
class DeviceBindState {
|
|
final bool isBound;
|
|
final String? mac;
|
|
final String? name;
|
|
final String? lastSync;
|
|
final bool isConnected;
|
|
final BpReading? lastReading;
|
|
|
|
const DeviceBindState({
|
|
this.isBound = false,
|
|
this.mac,
|
|
this.name,
|
|
this.lastSync,
|
|
this.isConnected = false,
|
|
this.lastReading,
|
|
});
|
|
|
|
DeviceBindState copyWith({
|
|
bool? isBound,
|
|
String? mac,
|
|
String? name,
|
|
String? lastSync,
|
|
bool? isConnected,
|
|
BpReading? lastReading,
|
|
}) => DeviceBindState(
|
|
isBound: isBound ?? this.isBound,
|
|
mac: mac ?? this.mac,
|
|
name: name ?? this.name,
|
|
lastSync: lastSync ?? this.lastSync,
|
|
isConnected: isConnected ?? this.isConnected,
|
|
lastReading: lastReading ?? this.lastReading,
|
|
);
|
|
}
|
|
|
|
class DeviceBindNotifier extends Notifier<DeviceBindState> {
|
|
StreamSubscription? _connSub;
|
|
|
|
@override
|
|
DeviceBindState build() {
|
|
_loadBinding();
|
|
_listenConnection();
|
|
return const DeviceBindState();
|
|
}
|
|
|
|
void _listenConnection() {
|
|
_connSub?.cancel();
|
|
_connSub = ref.read(omronBleServiceProvider).connectionState.listen((connected) {
|
|
if (state.isConnected != connected) {
|
|
state = state.copyWith(isConnected: connected);
|
|
}
|
|
if (!connected && state.isBound) {
|
|
// 设备断开,但保持绑定信息(下次可以重连)
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _loadBinding() async {
|
|
final db = ref.read(localDbProvider);
|
|
final mac = await db.read('bp_device_mac');
|
|
final name = await db.read('bp_device_name');
|
|
final lastSync = await db.read('bp_last_sync');
|
|
if (mac != null && mac.isNotEmpty) {
|
|
state = state.copyWith(isBound: true, mac: mac, name: name, lastSync: lastSync);
|
|
}
|
|
}
|
|
|
|
Future<void> bind(String mac, String name) async {
|
|
final db = ref.read(localDbProvider);
|
|
await db.write('bp_device_mac', mac);
|
|
await db.write('bp_device_name', name);
|
|
state = state.copyWith(isBound: true, mac: mac, name: name);
|
|
}
|
|
|
|
Future<void> unbind() async {
|
|
final db = ref.read(localDbProvider);
|
|
await db.delete('bp_device_mac');
|
|
await db.delete('bp_device_name');
|
|
await db.delete('bp_last_sync');
|
|
state = const DeviceBindState();
|
|
}
|
|
|
|
void setConnected(bool connected) {
|
|
state = state.copyWith(isConnected: connected);
|
|
}
|
|
|
|
Future<void> onReading(BpReading reading) async {
|
|
final db = ref.read(localDbProvider);
|
|
final lastSync = '${reading.timestamp.month}/${reading.timestamp.day} ${reading.timestamp.hour}:${reading.timestamp.minute.toString().padLeft(2, '0')}';
|
|
await db.write('bp_last_sync', lastSync);
|
|
state = state.copyWith(lastSync: lastSync, lastReading: reading);
|
|
ref.invalidate(latestHealthProvider);
|
|
}
|
|
}
|
|
|
|
final omronDeviceProvider = NotifierProvider<DeviceBindNotifier, DeviceBindState>(DeviceBindNotifier.new);
|