- Supports both Android and iOS.
- Small codebase, so breaking changes on the underlying system (ex. bluetooth related updates) can be easily upgraded.
- Easy to use, only a few functions to create a working app.
Add the following permissions to your AndroidManifest.xml file. To handle permissions you can use permission_handler package.
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />
Add the following properties to Info.plist in Xcode.
- Privacy - Bluetooth Always Usage Description
- Privacy - Bluetooth Peripheral Usage Description
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:simpleblue/model/bluetooth_device.dart';
import 'package:simpleblue/simpleblue.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
const serviceUUID = null;
const scanTimeout = 15000;
class _MyAppState extends State<MyApp> {
final _simplebluePlugin = Simpleblue();
bool _isBluetoothTurnedOn = false;
var devices = <String, BluetoothDevice>{};
String receivedData = '';
@override
void initState() {
super.initState();
_simplebluePlugin.listenConnectedDevice().listen((connectedDevice) {
debugPrint("Connected device: $connectedDevice");
if (connectedDevice != null) {
setState(() {
devices[connectedDevice.uuid] = connectedDevice;
});
}
connectedDevice?.stream?.listen((received) {
setState(() {
receivedData += "${DateTime.now().toString()}: $received\n";
});
});
}).onError((err) {
debugPrint(err);
});
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
scan();
});
_simplebluePlugin.getDevices().then((value) => setState(() {
for (var device in value) {
devices[device.uuid] = device;
}
}));
_simplebluePlugin.isTurnedOn().then((value) => setState(() {
_isBluetoothTurnedOn = value ?? false;
}));
_simplebluePlugin.listenStateChanges().listen((state) {
debugPrint('Bluetooth state changed to: $state');
setState(() {
_isBluetoothTurnedOn = state == SimpleblueState.on;
});
});
}
void scan() async {
final isBluetoothGranted = Platform.isIOS || (await Permission.bluetooth.status) == PermissionStatus.granted ||
(await Permission.bluetooth.request()) == PermissionStatus.granted;
if (isBluetoothGranted) {
print("Bluetooth permission granted");
final isLocationGranted = Platform.isIOS || (await Permission.location.status) == PermissionStatus.granted ||
(await Permission.location.request()) == PermissionStatus.granted;
if (isLocationGranted) {
print("Location permission granted");
_simplebluePlugin.scanDevices(serviceUUID: serviceUUID, timeout: scanTimeout).listen((event) {
setState(() {
for (var device in event) {
devices[device.uuid] = device;
}
});
});
}
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Column(children: [
if (_isBluetoothTurnedOn)
TextButton(
child: Text('Turn Off Bluetooth'),
onPressed: () {
_simplebluePlugin.turnOff();
}),
if (!_isBluetoothTurnedOn)
TextButton(
child: Text('Turn On Bluetooth'),
onPressed: () {
_simplebluePlugin.turnOn();
}),
TextButton(
child: Text('Get Devices'),
onPressed: () {
_simplebluePlugin.getDevices().then((value) {
setState(() {
for (var device in value) {
devices[device.uuid] = device;
}
});
});
}),
TextButton(
child: Text('Scan Devices'),
onPressed: () {
scan();
}),
Expanded(
child: buildDevices(),
),
SizedBox(
height: 200,
child: Align(
alignment: Alignment.topLeft,
child: Text(
receivedData,
style: const TextStyle(fontSize: 10),
)))
]),
),
);
}
final _connectingUUIDs = <String>[];
Widget buildDevices() {
final devList = devices.values.toList();
return ListView.builder(
itemCount: devList.length,
itemBuilder: (context, index) {
final device = devList[index];
return ListTile(
onTap: () {
if (device.isConnected) {
_simplebluePlugin.disconnect(device.uuid);
} else {
setState(() {
_connectingUUIDs.add(device.uuid);
});
_simplebluePlugin.connect(device.uuid).then((value) {
setState(() {
_connectingUUIDs.remove(device.uuid);
});
});
}
},
leading: _connectingUUIDs.contains(device.uuid)
? Icon(Icons.bluetooth, color: Colors.orange)
: (device.isConnected
? Icon(Icons.bluetooth_connected, color: Colors.blue)
: Icon(
Icons.bluetooth,
color: Colors.grey.shade300,
)),
title: Text('${device.name ?? 'No name'}\n${device.uuid}'),
subtitle: device.isConnected
? Row(
children: [
TextButton(
child: Text('Write 1'),
onPressed: () {
_simplebluePlugin.write(device.uuid, "sample data".codeUnits);
}),
TextButton(
child: Text('Write 2'),
onPressed: () {
_simplebluePlugin.write(device.uuid, "sample data 2".codeUnits);
})
],
)
: null,
);
});
}
}