You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 10:27:24