Flutter:收到蓝牙回调后如何更新自定义Stateful Widget状态
解决Flutter蓝牙响应后更新组件状态的问题
方案一:使用Stream实现组件与蓝牙回调的通信
1. 创建全局广播StreamController
用来传递蓝牙收到的msgID,让所有MeasurementPoint组件可以监听:
// 全局定义,比如放在蓝牙相关文件中 final bleResponseStream = StreamController<int>.broadcast();
2. 修改蓝牙数据解析函数,发送数据到Stream
当parseBleMsg解析出msgID后,将其添加到Stream中:
void parseBleMsg(List<int> data) { print("Data1: $data"); msgID = data[0]; luxValue = data[1]; print("lux: $luxValue"); print("msgid: $msgID"); // 将收到的msgID发送到Stream bleResponseStream.add(msgID); }
3. 修改MeasurementPoint组件,监听Stream更新状态
在组件的State类中监听Stream,当收到的msgID与自身id匹配时,更新状态:
class _MeasurementPointState extends State<MeasurementPoint> { bool pointState = false; StreamSubscription? _bleSubscription; @override void initState() { super.initState(); // 订阅蓝牙响应Stream _bleSubscription = bleResponseStream.listen((receivedMsgId) { if (receivedMsgId == widget.id) { setState(() { pointState = !pointState; }); } }); } @override void dispose() { // 取消订阅,避免内存泄漏 _bleSubscription?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Positioned( left: widget.leftPos, top: widget.topPos, child: ClipOval( child: Material( color: pointState ? Colors.green : Styles.primaryColor, child: InkWell( onTap: () async { // 发送蓝牙数据时,将当前按钮id传给设备,确保返回的msgID与id匹配 await Bluetooth().bleWrite(widget.id); lastClicked = widget.id; }, child: const SizedBox( width: 25, height: 25, child: Icon(Icons.sensors)), ), ), ), ); } }
4. 补充:修改蓝牙发送方法
让设备能收到按钮id,确保返回的msgID与点击的按钮对应:
// Bluetooth类中的bleWrite方法 void bleWrite(int pointId) async { // 假设colsTX是用于发送数据的蓝牙特征 await colsTX.write([pointId]); }
5. 关闭StreamController(避免内存泄漏)
在应用退出或不再需要时关闭Stream,比如在主页面的dispose中:
@override void dispose() { bleResponseStream.close(); super.dispose(); }
方案二:使用ChangeNotifier状态管理
适合需要统一管理多个组件状态的场景,结构更清晰。
1. 创建状态管理类
class MeasurementStateManager extends ChangeNotifier { // 存储每个测量点的状态,key为点的id final Map<int, bool> _pointStates = {}; // 获取指定id的点状态 bool getPointState(int id) => _pointStates[id] ?? false; // 更新指定id的点状态 void togglePointState(int id) { _pointStates[id] = !(_pointStates[id] ?? false); // 通知所有监听者更新 notifyListeners(); } }
2. 在应用顶层提供状态管理器
在main.dart中使用ChangeNotifierProvider注入管理器:
void main() { runApp( ChangeNotifierProvider( create: (context) => MeasurementStateManager(), child: const MyApp(), ), ); }
3. 修改蓝牙解析函数,触发状态更新
// 获取状态管理器实例(可通过Provider或全局方式) final stateManager = MeasurementStateManager(); void parseBleMsg(List<int> data) { print("Data1: $data"); msgID = data[0]; luxValue = data[1]; print("lux: $luxValue"); print("msgid: $msgID"); // 通知状态管理器更新对应id的点状态 stateManager.togglePointState(msgID); }
4. 修改MeasurementPoint组件,监听状态变化
class _MeasurementPointState extends State<MeasurementPoint> { late MeasurementStateManager _stateManager; @override void initState() { super.initState(); _stateManager = Provider.of<MeasurementStateManager>(context, listen: false); } @override Widget build(BuildContext context) { // 监听状态变化,自动重建组件 bool pointState = Provider.of<MeasurementStateManager>(context).getPointState(widget.id); return Positioned( left: widget.leftPos, top: widget.topPos, child: ClipOval( child: Material( color: pointState ? Colors.green : Styles.primaryColor, child: InkWell( onTap: () async { await Bluetooth().bleWrite(widget.id); lastClicked = widget.id; }, child: const SizedBox( width: 25, height: 25, child: Icon(Icons.sensors)), ), ), ), ); } }
注意事项
- 必须确保蓝牙设备返回的
msgID与点击的按钮id一一对应,否则无法正确匹配更新。 - 使用Stream时,务必在组件销毁时取消订阅;使用ChangeNotifier时,确保正确使用Provider的监听模式。
- 若需处理设备超时未响应的情况,可在点击按钮时启动定时器,超时后提示用户或重置状态。
内容的提问来源于stack exchange,提问作者Lushyy
相关产品推荐
相关产品推荐

