I'm developing a Flutter application where I need to connect and bond a Bluetooth Low Energy (BLE) device with a phone. I'm using the flutter_blue package for Bluetooth connectivity. While I can scan for devices and connect to them, I'm struggling with how to handle the bonding process.
Here's a snippet of my code for scanning and connecting to devices:
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class BluetoothLEManager extends StatefulWidget {
const BluetoothLEManager({Key? key}) : super(key: key);
@override
BluetoothLEManagerState createState() => BluetoothLEManagerState();
}
class BluetoothLEManagerState extends State<BluetoothLEManager> with SingleTickerProviderStateMixin {
FlutterBlue flutterBlue = FlutterBlue.instance;
List<BluetoothDevice> _devices = [];
bool _isSearching = false;
bool _scanCompleted = false;
@override
void initState() {
super.initState();
startBluetoothScan(isInitialScan: true);
}
@override
void dispose() {
flutterBlue.stopScan();
super.dispose();
}
void startBluetoothScan({bool isInitialScan = false}) {
if (_isSearching) return;
setState(() {
_isSearching = true;
_scanCompleted = false;
});
flutterBlue.startScan(timeout: const Duration(seconds: 10)).then((_) {
setState(() {
_isSearching = false;
_scanCompleted = true;
});
});
flutterBlue.scanResults.listen((results) {
setState(() {
_devices = results.map((r) => r.device).where((device) => device.name.isNotEmpty).toList();
});
});
flutterBlue.isScanning.listen((isScanning) {
if (!isScanning) {
print('Scanning stopped');
}
});
}
Future<void> connectToDevice(BluetoothDevice device) async {
await device.connect();
// further code to handle connection and service discovery
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BLE Devices'),
),
body: ListView.builder(
itemCount: _devices.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_devices[index].name),
onTap: () => connectToDevice(_devices[index]),
);
},
),
);
}
}
How do I bond a BLE device with the phone using the flutter_blue package and is there a specific method or process I should follow to ensure the device is bonded before establishing a connection?
Any guidance or example code would be greatly appreciated.
(What I've tried below)
Attempt to Check Bond State and Pair:
I tried to check the bond state of the device and initiate pairing if the device was not already paired. The code snippet I used was:
Future<void> checkAndPairDevice(BluetoothDevice device) async {
final BondState bondState = await device.bondState.first;
if (bondState == BondState.bonded) {
print('Device is already paired');
} else {
print('Device is not paired. Attempting to pair...');
try {
await device.pair();
print('Pairing successful');
} catch (error) {
print('Pairing failed: $error');
return;
}
}
}
Unfortunately, this approach did not work because the flutter_blue package does not support the bondState and pair methods directly. I encountered errors indicating these methods were undefined.
Switching to flutter_blue_plus Package:
I attempted to switch to the flutter_blue_plus package, which has more features and active maintenance. However, this resulted in numerous type conflicts and compatibility issues between flutter_blue and flutter_blue_plus types, making it impractical to integrate both packages.
Manual Pairing:
I also considered manually prompting the user to pair the devices via the system's Bluetooth settings before initiating the connection in the app. However, this approach does not provide a seamless user experience and does not ensure the app can programmatically verify and handle pairing.
The use of flutter_blue is deprecated due to irregular updates. Instead, we can use flutter_blue_plus, which is a fork of flutter_blue with more regular updates and minimal changes needed. Here’s how we can attempt bonding with a Bluetooth device using flutter_blue_plus:
import 'dart:io';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:device_info/device_info.dart';
Future<void> attemptBonding(BluetoothDevice device) async {
// get the device UUID
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
String deviceUUID;
try {
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
deviceUUID = androidInfo.id;
} else if (Platform.isIOS) {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
deviceUUID = iosInfo.identifierForVendor ?? '';
} else {
print('Unsupported platform');
return;
}
} catch (e) {
print('Error getting device UUID: $e');
return;
}
// convert the UUID to a byte array
List<int> uuidBytes = deviceUUID.codeUnits;
// function to match the UUID ignoring the first four characters (device name)
bool matchUUID(String characteristicUUID, String targetUUID) {
return characteristicUUID.substring(4).toLowerCase() == targetUUID.substring(4).toLowerCase();
}
// discover services and characteristics
List<BluetoothService> services;
try {
services = await device.discoverServices();
} catch (e) {
print('Error discovering services: $e');
return;
}
// find the bonding characteristic
BluetoothCharacteristic? bondingCharacteristic;
for (var service in services) {
bondingCharacteristic = service.characteristics.firstWhereOrNull(
(c) => matchUUID(c.uuid.toString(), 'UUID of your device that returns bonding characteristic')
);
if (bondingCharacteristic != null) break;
}
if (bondingCharacteristic != null) {
try {
print('Attempting to write to bonding characteristic...');
await bondingCharacteristic.write(uuidBytes, withoutResponse: true);
print('Written UUID to bonding characteristic.');
} catch (e) {
print('Error writing to bonding characteristic: $e');
return;
}
} else {
print('Bonding characteristic not found');
return;
}
// check bonding status
BluetoothCharacteristic? bondingStatusCharacteristic;
for (var service in services) {
bondingStatusCharacteristic = service.characteristics.firstWhereOrNull(
(c) => matchUUID(c.uuid.toString(), 'UUID of your device that returns bonding status characteristic')
);
if (bondingStatusCharacteristic != null) break;
}
if (bondingStatusCharacteristic != null) {
try {
await Future.delayed(Duration(seconds: 1)); // add a delay to ensure the bonding is processed
List<int> bondingStatus = await bondingStatusCharacteristic.read();
print('Bonding status value: $bondingStatus');
if (bondingStatus.isNotEmpty && bondingStatus[0] == 1) {
print('Bonding successful.');
} else {
print('Bonding failed or not recognized by the device.');
}
} catch (e) {
print('Error reading bonding status: $e');
}
} else {
print('Bonding status characteristic not found');
}
}
This approach ensures better compatibility and regular updates by using flutter_blue_plus, making the Bluetooth bonding process more reliable.