startScan method
Starts a scan for nearby Bluetooth Low Energy (BLE) devices and returns a stream of discovered devices.
Scanning for BLE devices is a crucial step in establishing a BLE connection. It allows the mobile app to discover nearby BLE devices and gather essential information like device name, MAC address, and more. This method starts the scanning operation on the platform side and listens for discovered devices.
The function takes optional filters
and settings
parameters that allow for more targeted device scanning.
For example, you could specify a filter to only discover devices that are advertising a specific service.
Similarly, settings
allows you to adjust aspects like scan mode, report delay, and more.
The method uses a StreamController to handle the asynchronous nature of BLE scanning. Every time a device is discovered by the native platform, the 'bleDeviceScanned' method is invoked, and the device information is parsed and added to the stream.
Implementation
@override
Stream<BleDevice> startScan({
List<ScanFilter>? filters,
ScanSettings? settings,
}) {
final StreamController<BleDevice> streamController = StreamController<BleDevice>.broadcast();
// Listen to the platform side for scanned devices.
channel.setMethodCallHandler((MethodCall call) async {
switch (call.method) {
case 'bleDeviceScanned':
try {
BleDevice device;
// Different operating systems will send the arguments in different formats. So, normalize the arguments
// as a Map<dynamic, dynamic> for use in the BleDevice.fromMap factory constructor.
if (call.arguments is String) {
final Map<dynamic, dynamic> argumentsParsed =
json.decode(call.arguments as String) as Map<dynamic, dynamic>;
device = BleDevice.fromMap(argumentsParsed);
} else {
device = BleDevice.fromMap(call.arguments as Map<dynamic, dynamic>);
}
streamController.add(device);
} catch (e) {
streamController.addError(
FormatException('Failed to parse discovered device info: $e'),
);
}
case 'error':
streamController.addError(Exception(call.arguments));
}
});
// Convert filters and settings into map representations if provided.
final List<Map<String, dynamic>>? filtersMap = filters?.map((filter) => filter.toMap()).toList();
final Map<String, dynamic>? settingsMap = settings?.toMap();
// Begin the scan on the platform side, including the filters and settings in the method call if provided.
channel.invokeMethod('startScan', {
'filters': filtersMap,
'settings': settingsMap,
});
return streamController.stream;
}