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

如何合并并过滤两个Dart流?flutter_blue蓝牙应用技术求助

嘿,我帮你梳理下怎么用rxdart合并flutter_blue的这两个流并添加过滤规则,其实核心是先把两个流统一成相同的数据类型,再合并处理,一步步来:

首先,确保你已经在pubspec.yaml里添加了rxdart依赖:

dependencies:
  flutter_blue: ^0.8.0 # 用你当前使用的版本
  rxdart: ^0.27.7 # 可以用最新稳定版

接下来,我们需要把connectedDevices(已连接设备流,类型是Stream<List<BluetoothDevice>>)和scanResults(扫描结果流,类型是Stream<List<ScanResult>>)转换成同类型的流,这样才能合并。扫描结果里的每个ScanResult都包含一个device属性,所以我们先把扫描流转成设备列表:

import 'package:flutter_blue/flutter_blue.dart';
import 'package:rxdart/rxdart.dart';

// 1. 处理已连接设备流,直接使用原流
final connectedDevicesStream = FlutterBlue.instance.connectedDevices;

// 2. 处理扫描结果流:提取设备、去重(扫描会重复输出同一设备)
final scannedDevicesStream = FlutterBlue.instance.scanResults.map((scanResults) {
  // 把ScanResult转成BluetoothDevice,然后用Set去重(基于设备ID)
  final uniqueScannedDevices = scanResults
      .map((result) => result.device)
      .toSet() // 自动去重
      .toList();
  return uniqueScannedDevices;
});

现在两个流都是Stream<List<BluetoothDevice>>了,接下来用rxdart的CombineLatestStream来合并它们——这个操作符会在任意一个流有新数据时,结合两个流的最新数据输出结果,刚好符合我们需要同时获取已连接和扫描设备的需求:

// 3. 合并两个流,合并后去重(避免已连接设备在扫描结果里重复)
final combinedDevicesStream = CombineLatestStream.combine2(
  connectedDevicesStream,
  scannedDevicesStream,
  (List<BluetoothDevice> connected, List<BluetoothDevice> scanned) {
    // 合并两个列表,再用Set去重
    final allDevices = [...connected, ...scanned];
    return allDevices.toSet().toList();
  },
);

然后就可以给合并后的流添加过滤规则了,比如过滤名称包含特定关键词、或者有特定UUID的设备:

// 4. 应用过滤规则,这里示例是保留名称非空且包含"MY_DEVICE"的设备
final filteredDevicesStream = combinedDevicesStream.map((devices) {
  return devices.where((device) {
    // 替换成你的过滤逻辑:比如检查设备名称、UUID、信号强度等
    return device.name.isNotEmpty && device.name.contains("MY_DEVICE");
  }).toList();
});

最后,在UI里用StreamBuilder来监听这个过滤后的流就可以了:

StreamBuilder<List<BluetoothDevice>>(
  stream: filteredDevicesStream,
  initialData: const [], // 初始空列表
  builder: (context, snapshot) {
    if (snapshot.hasError) {
      return Center(child: Text('Error: ${snapshot.error}'));
    }
    if (!snapshot.hasData) {
      return const Center(child: CircularProgressIndicator());
    }
    final devices = snapshot.data!;
    return ListView.builder(
      itemCount: devices.length,
      itemBuilder: (context, index) {
        final device = devices[index];
        return ListTile(
          title: Text(device.name.isNotEmpty ? device.name : 'Unknown Device'),
          subtitle: Text(device.id.toString()),
          trailing: device.isConnected
              ? const Icon(Icons.bluetooth_connected, color: Colors.green)
              : const Icon(Icons.bluetooth_disabled, color: Colors.grey),
        );
      },
    );
  },
);

别忘了,扫描需要主动启动,比如在页面初始化时调用:

@override
void initState() {
  super.initState();
  // 启动扫描,设置超时时间(可选)
  FlutterBlue.instance.startScan(timeout: const Duration(seconds: 10));
}

@override
void dispose() {
  // 页面销毁时停止扫描,避免资源浪费
  FlutterBlue.instance.stopScan();
  super.dispose();
}

几个关键点提醒:

  • 去重逻辑很重要,因为扫描流会持续输出同一设备的信息,已连接设备也可能出现在扫描结果里,用Set去重是最简单的方式(基于BluetoothDevice的==实现,默认比较设备ID)。
  • 如果你需要的是实时合并两个流的所有事件(而不是结合最新状态),可以用MergeStream,但通常CombineLatestStream更适合设备列表的场景。
  • 过滤逻辑可以灵活调整,比如根据ScanResult的信号强度(rssi)过滤,那你可能需要在转换扫描流时保留ScanResult而不是直接转成BluetoothDevice,然后在合并时处理成包含设备和信号的自定义类。

内容的提问来源于stack exchange,提问作者lg.lindstrom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 16:52:35