Overview
If you’re looking to connect a Bluetooth Low Energy (BLE) device to your Flutter app, then using the flutter_blue_plus package is a great way to do it. Previously, we discussed how to handle the Bluetooth state of your mobile device and perform operations based on changing of the Bluetooth state. In this blog, we’ll go over how to use the flutter_blue_plus package to scan for BLE devices, connect to them, and provide some updated code snippets to help you get started.
Implementation
To use the flutter_blue_plus package, you’ll first need to add it to your pubspec.yaml file. You can do this by adding the following line under the dependencies section:
dependencies:
flutter:
sdk: flutter
flutter_blue_plus: ^1.35.5
Next, you’ll need to import the package in your Dart code by adding the following line at the top of your file:
import ‘package:flutter_blue_plus/flutter_blue_plus.dart’;
Unlike the original flutter_blue package, flutter_blue_plus doesn’t require creating an instance. You can directly use the static methods provided by the FlutterBluePlus class:
// No need to create an instance – use static methods directly
// FlutterBluePlus.startScan(), FlutterBluePlus.scanResults, etc.
Scanning for BLE Devices
Basic Scanning Implementation:
class BLEScanner {
static List<ScanResult> scanResults = [];
static StreamSubscription<List<ScanResult>>? scanSubscription;
static bool isScanning = false;
static Future<void> startScan() async {
if (isScanning) return;
try {
isScanning = true;
scanResults.clear();
// Listen to scan results
scanSubscription = FlutterBluePlus.scanResults.listen((results) {
scanResults = results;
// Process each discovered device
for (ScanResult result in results) {
print(‘Device found: ${result.device.advName} – ${result.device.remoteId}’);
}
});
// Start scanning with timeout
await FlutterBluePlus.startScan(
timeout: const Duration(seconds: 15),
androidUsesFineLocation: true,
);
} catch (e) {
print(‘Error starting scan: $e’);
} finally {
isScanning = false;
}
}
static Future<void> stopScan() async {
await FlutterBluePlus.stopScan();
scanSubscription?.cancel();
isScanning = false;
}
}
Advanced Scanning with Filters:
flutter_blue_plus provides various filtering options to help you find specific devices more efficiently:
static Future<void> startScanWithFilters() async {
try {
// Listen to scan results
scanSubscription = FlutterBluePlus.scanResults.listen((results) {
for (ScanResult result in results) {
print(‘Filtered device: ${result.device.advName} – RSSI: ${result.rssi}’);
}
});
// Start scanning with filters
await FlutterBluePlus.startScan(
timeout: const Duration(seconds: 15),
withServices: [Guid(“180D”)], // Filter by Heart Rate Service
withNames: [“MyDevice”, “SensorHub”], // Filter by device names
androidUsesFineLocation: true,
);
} catch (e) {
print(‘Error starting filtered scan: $e’);
}
}
Finding a Specific Device:
Once you have the list of ScanResult objects, you can iterate through the list and find the device you want to connect to. Here’s an updated approach:
static ScanResult? findTargetDevice(String targetDeviceName) {
for (ScanResult result in scanResults) {
// Check device name (advertising name)
if (result.device.advName == targetDeviceName) {
return result;
}
// Alternative: check by remote ID
if (result.device.remoteId.toString() == targetDeviceName) {
return result;
}
}
return null;
}
Connecting to BLE Devices
Once you have found the device you want to connect to, you can call the connect method on the BluetoothDevice object. The connection process in flutter_blue_plus is more robust with better error handling:
class BLEConnectionManager {
static BluetoothDevice? connectedDevice;
static StreamSubscription<BluetoothConnectionState>? connectionSubscription;
static Future<bool> connectToDevice(BluetoothDevice device) async {
try {
// Connect to the device with timeout
await device.connect(
timeout: const Duration(seconds: 35),
autoConnect: false,
);
// Listen to connection state changes
connectionSubscription = device.connectionState.listen((state) {
print(‘Connection State: $state’);
switch (state) {
case BluetoothConnectionState.connected:
connectedDevice = device;
print(‘Successfully connected to ${device.advName}’);
break;
case BluetoothConnectionState.disconnected:
connectedDevice = null;
print(‘Disconnected from ${device.advName}’);
break;
case BluetoothConnectionState.connecting:
print(‘Connecting to ${device.advName}…’);
break;
case BluetoothConnectionState.disconnecting:
print(‘Disconnecting from ${device.advName}…’);
break;
}
});
return true;
} catch (e) {
print(‘Error connecting to device: $e’);
return false;
}
}
static Future<void> disconnect() async {
if (connectedDevice != null) {
try {
await connectedDevice!.disconnect();
connectionSubscription?.cancel();
connectedDevice = null;
} catch (e) {
print(‘Error disconnecting: $e’);
}
}
}
}
Complete Example Implementation
Here’s a complete example showing how to integrate scanning and connection in a Flutter widget:
class BLEDeviceScreen extends StatefulWidget {
const BLEDeviceScreen({Key? key}) : super(key: key);
@override
State<BLEDeviceScreen> createState() => _BLEDeviceScreenState();
}
class _BLEDeviceScreenState extends State<BLEDeviceScreen> {
List<ScanResult> scanResults = [];
BluetoothDevice? connectedDevice;
bool isScanning = false;
StreamSubscription<List<ScanResult>>? scanSubscription;
StreamSubscription<BluetoothConnectionState>? connectionSubscription;
@override
void initState() {
super.initState();
_initializeBLE();
}
void _initializeBLE() {
// Listen to scan results
scanSubscription = FlutterBluePlus.scanResults.listen((results) {
if (mounted) {
setState(() {
scanResults = results;
});
}
});
}
Future<void> _startScan() async {
if (isScanning) return;
setState(() {
isScanning = true;
scanResults.clear();
});
try {
await FlutterBluePlus.startScan(
timeout: const Duration(seconds: 15),
androidUsesFineLocation: true,
);
} catch (e) {
print(‘Error starting scan: $e’);
}
setState(() {
isScanning = false;
});
}
Future<void> _connectToDevice(BluetoothDevice device) async {
try {
await device.connect(timeout: const Duration(seconds: 35));
connectionSubscription = device.connectionState.listen((state) {
if (mounted) {
setState(() {
connectedDevice = (state == BluetoothConnectionState.connected) ? device : null;
});
}
});
} catch (e) {
print(‘Error connecting: $e’);
}
}
@override
void dispose() {
scanSubscription?.cancel();
connectionSubscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(‘BLE Devices’),
actions: [
IconButton(
icon: Icon(isScanning ? Icons.stop : Icons.search),
onPressed: isScanning ? null : _startScan,
),
],
),
body: Column(
children: [
// Connection Status
if (connectedDevice != null)
Container(
padding: const EdgeInsets.all(16),
color: Colors.green,
width: double.infinity,
child: Text(
‘Connected to: ${connectedDevice!.advName}’,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
),
// Scan Button
Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
onPressed: isScanning ? null : _startScan,
child: Text(isScanning ? ‘Scanning…’ : ‘Start Scan’),
),
),
// Device List
Expanded(
child: scanResults.isEmpty
? const Center(child: Text(‘No devices found’))
: ListView.builder(
itemCount: scanResults.length,
itemBuilder: (context, index) {
final result = scanResults[index];
final device = result.device;
return ListTile(
leading: const Icon(Icons.bluetooth),
title: Text(device.advName.isNotEmpty ? device.advName : device.remoteId.toString()),
subtitle: Text(‘RSSI: ${result.rssi}’),
trailing: connectedDevice?.remoteId == device.remoteId
? const Icon(Icons.check_circle, color: Colors.green)
: ElevatedButton(
onPressed: () => _connectToDevice(device),
child: const Text(‘Connect’),
),
);
},
),
),
],
),
);
}
}
Basic Device Interaction
Now that you’re connected to the BLE device, you can start interacting with it. Here’s a preview of basic operations (we’ll cover these in detail in the next parts):
class BLEDeviceInteraction {
static Future<List<BluetoothService>> discoverServices(BluetoothDevice device) async {
try {
return await device.discoverServices();
} catch (e) {
print(‘Error discovering services: $e’);
return [];
}
}
static Future<List<int>> readCharacteristic(BluetoothCharacteristic characteristic) async {
try {
return await characteristic.read();
} catch (e) {
print(‘Error reading characteristic: $e’);
return [];
}
}
static Future<void> writeCharacteristic(BluetoothCharacteristic characteristic, List<int> value) async {
try {
await characteristic.write(value);
} catch (e) {
print(‘Error writing characteristic: $e’);
}
}
}
Error Handling and Best Practices
When working with BLE connections, it’s important to handle errors gracefully:
class BLEErrorHandler {
static Future<bool> handleConnection(BluetoothDevice device) async {
try {
await device.connect(timeout: const Duration(seconds: 35));
return true;
} on TimeoutException {
print(‘Connection timeout – device may be out of range’);
return false;
} on PlatformException catch (e) {
print(‘Platform exception: ${e.message}’);
return false;
} catch (e) {
print(‘Unexpected error: $e’);
return false;
}
}
static Future<void> safeScan() async {
try {
await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
} on PlatformException catch (e) {
if (e.code == ‘scan_too_frequent’) {
print(‘Scanning too frequently – waiting before retry’);
await Future.delayed(const Duration(seconds: 2));
}
}
}
}
Seamless BLE Implementation for Flutter Apps
Delve deeper into connecting BLE devices with Flutter. Ready to implement these insights in your app? Connect with AlphaBOLD for custom development expert guidance and seamless BLE implementation.
Request a ConsultationConclusion
In conclusion, connecting to Bluetooth Low Energy devices in Flutter using the latest flutter_blue_plus package is a straightforward process that provides robust functionality for BLE operations. Whether you’re building a mobile app for iOS or Android, the Flutter framework combined with flutter_blue_plus provides all the tools you need to easily scan for, connect to, and interact with BLE devices.
Key improvements in this updated version include:
- Modern flutter_blue_plus 1.35.5 API usage
- Proper stream-based scanning implementation
- Enhanced connection management with state monitoring
- Better error handling and timeout management
- Improved filtering and device discovery
- More robust code structure and organization
By using the updated flutter_blue_plus package, you can take advantage of improved stability, better performance, and enhanced feature set. The package handles the complexity of BLE communication while providing a clean, easy-to-use API that allows you to focus on building your app’s unique features and functionality.
In the next part of our series, we’ll dive deeper into BLE communication, covering service discovery, characteristic operations, and advanced data handling techniques.
We hope you found our article informative and comprehensive, and hope you stick around for more. We hope to see you again soon .
Explore Recent Blog Posts








