Flutter蓝牙通信插件ble_sdk的使用
Flutter蓝牙通信插件ble_sdk的使用
ble_sdk
<img src="https://img.shields.io/pub/v/ble_sdk?label=ble_sdk" alt="ble_sdk version">
<img src="https://img.shields.io/github/repo-size/ho-doan/ble_sdk" alt="ble_sdk size">
<img src="https://img.shields.io/github/issues/ho-doan/ble_sdk" alt="ble_sdk issues">
<img src="https://img.shields.io/pub/likes/ble_sdk" alt="ble_sdk issues">
- Bluetooth Low Energy (BLE) 插件,可以与单个设备通信。
Futures
- BLE 设备发现
- 连接 BLE
- BLE 状态
- 连接 BLE 状态
- 发现服务
- 启用通知一个特征值
- 启用指示一个特征值
- 读取一个特征值
- 写入一个特征值
Getting Started
android
Android ProGuard 规则
-keep class com.hodoan.ble_sdk.** { *; }
ios
<key>NSBluetoothAlwaysUsageDescription</key>
<string>使用 BLE</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>使用 BLE</string>
Usage
扫描设备
BleSdk.instance.startScan(services: ['1808']);
停止扫描设备
BleSdk.instance.stopScan();
连接设备
BleSdk.instance.connect(deviceId: '...');
设备发现
BleSdk.instance.discoverServices();
设置通知一个特征值
BleSdk.instance.setNotification(Characteristic(
characteristicId: '...',
serviceId: '...',
properties: [],
));
设置指示一个特征值
BleSdk.instance.setIndication(Characteristic(
characteristicId: '...',
serviceId: '...',
properties: [],
));
读取一个特征值
BleSdk.instance.readCharacteristic(Characteristic(
characteristicId: '...',
serviceId: '...',
properties: [],
));
写入一个特征值
BleSdk.instance.readCharacteristic(CharacteristicValue(
characteristic: ...,
data: [],
));
断开设备连接
BleSdk.instance.disconnect();
日志特征值
BleSdk.instance.logResult.listen((_){});
监听读写所有特征值
BleSdk.instance.characteristicResult.listen((_){});
监听 BLE 开关状态
BleSdk.instance.stateBluetoothResult.listen((_){});
连接状态
BleSdk.instance.stateConnectResult.listen((_){});
示例代码
import 'dart:async';
import 'dart:developer';
import 'package:ble_sdk/ble_sdk.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: ScanPage()));
}
class ScanPage extends StatefulWidget {
const ScanPage({super.key});
[@override](/user/override)
State<ScanPage> createState() => _ScanPageState();
}
class _ScanPageState extends State<ScanPage> {
late StreamSubscription deviceStream;
bool isScan = false;
[@override](/user/override)
void initState() {
super.initState();
BleSdk.instance
.isBluetoothAvailable()
.then((value) => log(value.toString()));
}
[@override](/user/override)
void dispose() {
if (isScan) {
deviceStream.cancel();
}
super.dispose();
}
final ctlServices = TextEditingController(text: '1808');
List<BluetoothBLEModel> devices = [];
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
const SliverAppBar(
title: Text('Ble Sdk'),
),
SliverToBoxAdapter(
child: Row(
children: [
Expanded(
child: TextFormField(
decoration: const InputDecoration(
hintText: 'servicesUUID ex: 1808,1810',
),
validator: (s) =>
s == null || !s.split(',').any((e) => e.length != 4)
? null
: 'server is not validator',
autovalidateMode: AutovalidateMode.always,
style: const TextStyle(fontSize: 12),
controller: ctlServices,
),
),
GestureDetector(
onTap: () {
if (!ctlServices.text
.split(',')
.any((e) => e.length != 4)) {
setState(() => isScan = true);
BleSdk.instance.startScan(
services: ctlServices.text
.split(',')
.where((s) => s.length == 4)
.toList(),
);
setState(() => devices = []);
deviceStream =
BleSdk.instance.deviceResult.listen((event) {
setState(
() => devices = List.from(devices)..add(event),
);
});
}
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 6,
),
color: Colors.blue,
child: const Text(
'Scan',
),
),
),
const SizedBox(width: 2),
GestureDetector(
onTap: () {
BleSdk.instance.stopScan();
if (isScan) {
deviceStream.cancel();
setState(() => isScan = false);
}
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 6,
),
color: Colors.blue,
child: const Text(
'Stop',
),
),
),
const SizedBox(width: 2),
],
),
),
SliverList(
delegate: SliverChildListDelegate(
[
for (final device in devices)
Container(
color: Colors.grey,
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 16,
),
child: Row(
children: [
Expanded(
child: Text('${device.name}(${device.id})'),
),
GestureDetector(
onTap: () async {
BleSdk.instance
.connect(deviceId: device.id)
.then((value) {
log('message $value');
if (value) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => DevicePage(device: device),
),
);
}
});
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 6,
),
color: Colors.blue,
child: const Text(
'Connect',
),
),
),
],
),
),
],
),
),
],
),
);
}
}
class DevicePage extends StatefulWidget {
const DevicePage({
super.key,
required this.device,
});
final BluetoothBLEModel device;
[@override](/user/override)
State<DevicePage> createState() => _DevicePageState();
}
class _DevicePageState extends State<DevicePage> {
List<Service> _services = [];
List<String> _logs = [];
late StreamSubscription logStream;
late StreamSubscription subscription;
[@override](/user/override)
void initState() {
logStream = BleSdk.instance.logResult.listen((event) {
setState(() => _logs = List.from(_logs)
..add(
'${event.characteristic.characteristicId.replaceAll('0000', '').split('-').first}: ${event.message}'));
});
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
subscription = BleSdk.instance.stateConnectResult.listen((event) {
if (event == StateConnect.disconnected) {
Navigator.of(context).pop();
}
});
});
}
[@override](/user/override)
void dispose() {
subscription.cancel();
logStream.cancel();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
title: Text(
'Device ${widget.device.name}\n(${widget.device.id}) -- connected',
),
),
SliverToBoxAdapter(
child: Row(
children: [
GestureDetector(
onTap: () async {
final services = await BleSdk.instance.discoverServices();
setState(
() => _services = services,
);
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 6,
),
color: Colors.blue,
child: const Text(
'Discover',
),
),
),
const SizedBox(width: 2),
GestureDetector(
onTap: () => BleSdk.instance.disconnect().then(
(value) => Navigator.of(context).pop(),
),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 6,
),
color: Colors.blue,
child: const Text(
'Disconnect',
),
),
),
const Spacer(),
GestureDetector(
onTap: () => showDialog(
context: context,
builder: (ctx) => Material(
type: MaterialType.transparency,
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
color: Colors.white,
child: ListView(
children: [
for (final log in _logs) Text(log),
],
),
),
),
),
),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 6,
),
color: Colors.blue,
child: const Text(
'Log',
),
),
),
],
),
),
SliverList(
delegate: SliverChildListDelegate(
[
for (final service in _services)
ServiceWidget(service: service),
],
),
),
],
),
);
}
}
class ServiceWidget extends StatelessWidget {
const ServiceWidget({
super.key,
required this.service,
});
final Service service;
[@override](/user/override)
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.grey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(8),
child: Text(
'S: ${service.serviceId.replaceFirst('0000', '').split('-').first}'),
),
for (final char in service.characteristics)
CharacteristicWidget(char: char),
],
),
),
);
}
}
class CharacteristicWidget extends StatefulWidget {
const CharacteristicWidget({
super.key,
required this.char,
});
final Characteristic char;
[@override](/user/override)
State<CharacteristicWidget> createState() => _CharacteristicWidgetState();
}
class _CharacteristicWidgetState extends State<CharacteristicWidget> {
[@override](/user/override)
void initState() {
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(4).copyWith(left: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: Row(
children: [
Expanded(
child: Text(
'- C: ${widget.char.characteristicId.replaceAll('0000', '').split('-').first}(${widget.char.properties.map((e) => e.name).join(',')})',
),
),
SizedBox(
width: 120,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
if (widget.char.properties
.contains(CharacteristicProperties.READ))
GestureDetector(
onTap: () => BleSdk.instance.readCharacteristic(widget.char),
child: const Icon(
Icons.read_more,
size: 28,
color: Colors.white,
),
),
if (widget.char.properties
.contains(CharacteristicProperties.WRITE))
GestureDetector(
onTap: () => _write(widget.char),
child: const Icon(
Icons.edit_note,
size: 28,
color: Colors.green,
),
),
if (widget.char.properties
.contains(CharacteristicProperties.NOTIFY))
GestureDetector(
onTap: () => BleSdk.instance.setNotification(widget.char),
child: const Icon(
Icons.notifications_on,
size: 28,
color: Colors.redAccent,
),
),
if (widget.char.properties
.contains(CharacteristicProperties.INDICATE))
GestureDetector(
onTap: () => BleSdk.instance.setNotification(widget.char),
child: const Icon(
Icons.drag_indicator_rounded,
size: 28,
color: Colors.redAccent,
),
),
],
),
),
],
),
);
}
_write(Characteristic char) {
showDialog(
context: context,
builder: (context) => Material(
type: MaterialType.transparency,
child: Center(
child: WriteCharacteristicWidget(char: widget.char),
),
),
);
}
}
class WriteCharacteristicWidget extends StatefulWidget {
const WriteCharacteristicWidget({
super.key,
required this.char,
});
final Characteristic char;
[@override](/user/override)
State<WriteCharacteristicWidget> createState() => _WriteCharacteristicWidgetState();
}
class _WriteCharacteristicWidgetState extends State<WriteCharacteristicWidget> {
final ctlValue = TextEditingController(text: '4');
[@override](/user/override)
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width - 50,
padding: const EdgeInsets.all(10),
height: 300,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Write value characteristic: ${widget.char.characteristicId.replaceAll('0000', '').split('-').first}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const Spacer(),
TextFormField(
controller: ctlValue,
decoration: const InputDecoration(
hintText: '0 or 0,1,2,3,4 ...',
border: OutlineInputBorder(),
),
),
const Spacer(),
GestureDetector(
onTap: () => BleSdk.instance.writeCharacteristic(
CharacteristicValue(
characteristic: widget.char,
data: ctlValue.text.split(',').map(
(e) => int.parse(e),
),
),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.green,
),
borderRadius: BorderRadius.circular(8),
color: Colors.blue,
),
padding: const EdgeInsets.symmetric(
vertical: 6,
horizontal: 20,
),
child: const Text(
'Write',
),
),
),
],
),
);
}
}
更多关于Flutter蓝牙通信插件ble_sdk的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
1 回复
更多关于Flutter蓝牙通信插件ble_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
ble_sdk
是一个用于在 Flutter 应用中实现低功耗蓝牙(BLE)通信的插件。使用该插件,开发者可以轻松地在 Flutter 应用中扫描、连接、读写 BLE 设备。以下是使用 ble_sdk
插件进行蓝牙通信的基本步骤和代码示例。
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 ble_sdk
依赖:
dependencies:
flutter:
sdk: flutter
ble_sdk: ^1.0.0 # 使用最新版本
然后运行 flutter pub get
来安装依赖。
2. 初始化蓝牙
在使用蓝牙功能之前,需要先初始化蓝牙适配器:
import 'package:ble_sdk/ble_sdk.dart';
void initBluetooth() async {
bool isAvailable = await BleSdk.instance.isAvailable();
if (!isAvailable) {
print("Bluetooth is not available on this device.");
return;
}
bool isEnabled = await BleSdk.instance.isEnabled();
if (!isEnabled) {
print("Bluetooth is not enabled.");
return;
}
print("Bluetooth is available and enabled.");
}
3. 扫描设备
可以使用 startScan
方法来扫描附近的 BLE 设备:
void scanDevices() async {
BleSdk.instance.startScan(
onDeviceFound: (device) {
print("Found device: ${device.name} - ${device.id}");
},
onScanFinished: (devices) {
print("Scan finished. Total devices found: ${devices.length}");
},
onError: (error) {
print("Scan error: $error");
},
);
}
4. 连接设备
扫描到设备后,可以使用 connect
方法来连接设备:
void connectDevice(String deviceId) async {
await BleSdk.instance.connect(
deviceId,
onConnected: (device) {
print("Connected to device: ${device.name}");
},
onDisconnected: (device) {
print("Disconnected from device: ${device.name}");
},
onError: (error) {
print("Connection error: $error");
},
);
}
5. 读取和写入数据
连接设备后,可以读取和写入数据。首先需要发现服务和特征:
void discoverServices(String deviceId) async {
List<BleService> services = await BleSdk.instance.discoverServices(deviceId);
for (var service in services) {
print("Service: ${service.uuid}");
for (var characteristic in service.characteristics) {
print(" Characteristic: ${characteristic.uuid}");
}
}
}
读取数据:
void readCharacteristic(String deviceId, String serviceUuid, String characteristicUuid) async {
List<int> value = await BleSdk.instance.readCharacteristic(deviceId, serviceUuid, characteristicUuid);
print("Read value: $value");
}
写入数据:
void writeCharacteristic(String deviceId, String serviceUuid, String characteristicUuid, List<int> value) async {
await BleSdk.instance.writeCharacteristic(deviceId, serviceUuid, characteristicUuid, value);
print("Write value: $value");
}
6. 断开连接
使用 disconnect
方法断开与设备的连接:
void disconnectDevice(String deviceId) async {
await BleSdk.instance.disconnect(deviceId);
print("Disconnected from device: $deviceId");
}
7. 停止扫描
扫描完成后,可以使用 stopScan
方法停止扫描:
void stopScanning() {
BleSdk.instance.stopScan();
print("Scan stopped.");
}
8. 权限处理
在 Android 和 iOS 上,使用蓝牙功能需要请求相应的权限。确保在 AndroidManifest.xml
和 Info.plist
中添加必要的权限配置。
9. 处理蓝牙状态变化
蓝牙状态可能会发生变化(例如用户关闭蓝牙),可以监听这些变化:
void listenToBluetoothState() {
BleSdk.instance.onStateChanged.listen((state) {
print("Bluetooth state changed to: $state");
});
}