如何合并并过滤两个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
相关产品推荐
相关产品推荐

