目录
权限配置(android/app/src/main/AndroidManifest.xml)
编译配置(android/app/build.gradle)
StatelessWidget vs StatefulWidget 设计说明
1. 蓝牙技术概述
1.1 蓝牙技术分类
蓝牙技术主要分为两大类:
-
经典蓝牙(Classic Bluetooth)
-
适用于音频传输、文件传输等高带宽应用
-
功耗相对较高
-
传输距离通常在10米以内
-
-
低功耗蓝牙(BLE - Bluetooth Low Energy)
-
专为物联网设备设计
-
功耗极低,适合长期运行
-
传输数据量小但频率高
-
1.2 蓝牙通信架构

1.3 Flutter蓝牙开发流程图

1.4 蓝牙开发最佳实践思维导图

2. Flutter蓝牙插件介绍
2.1 主要插件对比
| 插件名称 | 功能特点 | 适用场景 | 维护状态 |
|---|---|---|---|
| flutter_bluetooth_serial | 经典蓝牙串口通信 | 简单数据传输 | 活跃 |
| flutter_blue_plus | BLE专用,功能全面 | IoT设备连接 | 非常活跃 |
| bluetooth_classic | 经典蓝牙全功能 | 音频、文件传输 | 一般 |
| permission_handler | 权限管理 | 权限申请 | 活跃 |
2.2 推荐插件组合
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
# BLE通信(推荐)
flutter_blue_plus: ^1.14.6
# 经典蓝牙串口通信
flutter_bluetooth_serial: ^0.4.0
# 权限管理
permission_handler: ^11.0.1
# 设备信息
device_info_plus: ^9.1.0
# 位置服务(BLE需要)
geolocator: ^9.0.2
3. 环境配置与权限设置
3.1 Android配置
权限配置(android/app/src/main/AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 蓝牙基础权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12+ 新权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- 位置权限(BLE扫描需要) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 声明蓝牙功能 -->
<uses-feature
android:name="android.hardware.bluetooth"
android:required="true" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<application>
<!-- 应用配置 -->
</application>
</manifest>
编译配置(android/app/build.gradle)
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 21 // 蓝牙BLE最低要求
targetSdkVersion 34
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
3.2 iOS配置
权限配置(ios/Runner/Info.plist)
<dict>
<!-- 蓝牙使用说明 -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>此应用需要使用蓝牙功能来连接外部设备</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>此应用需要使用蓝牙功能来连接外部设备</string>
<!-- 位置权限说明(BLE扫描需要) -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>此应用需要位置权限来扫描蓝牙设备</string>
<!-- 后台模式 -->
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>
</dict>
3.3 权限管理

import 'package:permission_handler.dart';
import 'package:device_info_plus/device_info_plus.dart';
/// 蓝牙权限管理器
class BluetoothPermissionManager {
/// 请求蓝牙相关权限,根据不同平台和Android版本申请对应的权限
static Future<bool> requestBluetoothPermissions() async {
// 获取设备信息插件实例,用于判断设备类型和版本
final deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
// Android 12(API 31)及以上版本需要申请新的蓝牙权限
if (androidInfo.version.sdkInt >= 31) {
return await _requestAndroid12Permissions();
} else {
// Android 12以下版本使用传统的蓝牙权限
return await _requestLegacyAndroidPermissions();
}
} else if (Platform.isIOS) {
return await _requestIOSPermissions();
}
// 不支持的平台返回false
return false;
}
///Android 12引入了新的蓝牙权限模型,需要申请更细粒度的权限
static Future<bool> _requestAndroid12Permissions() async {
// 定义Android 12+需要的权限列表
final permissions = [
Permission.bluetoothScan, // 蓝牙扫描权限
Permission.bluetoothConnect, // 蓝牙连接权限
Permission.bluetoothAdvertise, // 蓝牙广播权限
Permission.locationWhenInUse, // 位置权限(BLE扫描需要)
];
// 批量请求所有权限
final statuses = await permissions.request();
// 检查是否所有权限都被授予
// every()方法检查集合中的每个元素是否都满足条件
return statuses.values.every(
(status) => status == PermissionStatus.granted
);
}
/// 请求Android 12以下版本的传统蓝牙权限,旧版本Android使用更简单的权限模型
static Future<bool> _requestLegacyAndroidPermissions() async {
// 定义传统Android版本需要的权限列表
final permissions = [
Permission.bluetooth, // 基础蓝牙权限
Permission.locationWhenInUse, // 位置权限(BLE扫描需要)
];
final statuses = await permissions.request();
return statuses.values.every(
(status) => status == PermissionStatus.granted
);
}
/// 请求iOS平台的蓝牙权限
/// iOS的蓝牙权限相对简单,只需要基础蓝牙权限
static Future<bool> _requestIOSPermissions() async {
// 请求蓝牙权限
final status = await Permission.bluetooth.request();
// 返回权限是否被授予
return status == PermissionStatus.granted;
}
/// 检查当前蓝牙权限状态
/// 平台差异:
/// - Android 12+(API 31): 检查 bluetoothScan 和 bluetoothConnect 权限
/// - Android 12以下: 检查传统的 bluetooth 权限
/// - iOS: 检查基础 bluetooth 权限
static Future<bool> checkBluetoothPermissions() async {
if (Platform.isAndroid) {
// 获取设备信息插件实例,用于判断Android版本
final deviceInfo = DeviceInfoPlugin();
final androidInfo = await deviceInfo.androidInfo;
if (androidInfo.version.sdkInt >= 31) {
return await Permission.bluetoothScan.isGranted &&
await Permission.bluetoothConnect.isGranted;
} else {
return await Permission.bluetooth.isGranted;
}
}
else if (Platform.isIOS) {
return await Permission.bluetooth.isGranted;
}
return false;
}
}
4. 蓝牙基础操作
4.1 蓝牙状态管理

import 'package:flutter_blue_plus/flutter_blue_plus.dart';
/// 蓝牙状态管理器
/// 使用flutter_blue_plus插件管理BLE蓝牙适配器状态
class BluetoothStateManager {
/// 获取蓝牙适配器状态变化的流
/// 可以实时监听蓝牙开启、关闭、不可用等状态变化
static Stream<BluetoothAdapterState> get adapterStateStream =>
FlutterBluePlus.adapterState;
/// 获取当前蓝牙适配器状态
/// 返回当前时刻的蓝牙状态(开启、关闭、不可用等)
static Future<BluetoothAdapterState> get adapterState =>
FlutterBluePlus.adapterState.first;
/// 检查设备是否支持蓝牙
/// 返回true表示设备支持蓝牙功能,false表示不支持
static Future<bool> get isSupported => FlutterBluePlus.isSupported;
/// 获取蓝牙适配器名称
/// 返回本设备的蓝牙适配器名称,通常是设备名称
static Future<String> get adapterName => FlutterBluePlus.adapterName;
/// 检查蓝牙是否可用
/// 综合判断设备是否支持蓝牙且蓝牙已开启
/// 返回true表示蓝牙可以正常使用
static Future<bool> isBluetoothAvailable() async {
// 首先检查设备是否支持蓝牙
if (!await isSupported) {
return false;
}
// 获取当前蓝牙状态
final state = await adapterState;
// 判断蓝牙是否已开启
return state == BluetoothAdapterState.on;
}
/// 请求开启蓝牙
/// 在Android平台上可以程序化开启蓝牙,iOS需要用户手动开启
/// 返回true表示开启成功或已开启,false表示开启失败
static Future<bool> requestEnableBluetooth() async {
try {
// 只有Android平台支持程序化开启蓝牙
if (Platform.isAndroid) {
await FlutterBluePlus.turnOn();
}
return true;
} catch (e) {
// 捕获开启蓝牙时的异常(如用户拒绝、权限不足等)
print('无法开启蓝牙: $e');
return false;
}
}
}
4.2 蓝牙状态监听Widget

StatelessWidget vs StatefulWidget 设计说明

/// 蓝牙状态监听Widget
/// 根据蓝牙状态自动切换显示不同的界面内容
/// 当蓝牙开启时显示主要内容,关闭时显示提示界面
class BluetoothStateWidget extends StatelessWidget {
/// 蓝牙开启时显示的主要内容Widget
final Widget child;
/// 蓝牙关闭时显示的自定义Widget,如果为null则使用默认界面
final Widget? bluetoothOffWidget;
/// 蓝牙不支持时显示的自定义Widget,如果为null则使用默认界面
final Widget? bluetoothUnsupportedWidget;
const BluetoothStateWidget({
Key? key,
required this.child,
this.bluetoothOffWidget,
this.bluetoothUnsupportedWidget,
}) : super(key: key);
@override
Widget build(BuildContext context) {
// 使用StreamBuilder监听蓝牙状态变化
return StreamBuilder<BluetoothAdapterState>(
stream: FlutterBluePlus.adapterState, // 蓝牙状态流
initialData: BluetoothAdapterState.unknown, // 初始状态为未知
builder: (context, snapshot) {
final state = snapshot.data; // 获取当前蓝牙状态
// 根据不同的蓝牙状态显示不同的界面
switch (state) {
case BluetoothAdapterState.on:
// 蓝牙已开启,显示主要内容
return child;
case BluetoothAdapterState.off:
// 蓝牙已关闭,显示关闭提示界面
return bluetoothOffWidget ?? _buildBluetoothOffWidget();
case BluetoothAdapterState.unavailable:
// 蓝牙不可用,显示不支持提示界面
return bluetoothUnsupportedWidget ?? _buildUnsupportedWidget();
default:
// 未知状态或正在切换状态,显示加载指示器
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
Widget _buildBluetoothOffWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.bluetooth_disabled,
size: 64,
color: Colors.grey,
),
const SizedBox(height: 16),
const Text(
'蓝牙未开启',
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () async {
await BluetoothStateManager.requestEnableBluetooth();
},
child: const Text('开启蓝牙'),
),
],
),
);
}
Widget _buildUnsupportedWidget() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.bluetooth_disabled,
size: 64,
color: Colors.red,
),
SizedBox(height: 16),
Text(
'设备不支持蓝牙',
style: TextStyle(fontSize: 18),
),
],
),
);
}
}
5. BLE(低功耗蓝牙)详解
5.1 BLE通信架构

5.2 BLE设备扫描流程

/// BLE设备扫描器
/// 负责扫描附近的BLE设备并管理扫描结果
class BLEScanner {
/// 存储扫描到的设备列表,避免重复添加相同设备
static final List<BluetoothDevice> _scanResults = [];
/// 扫描结果订阅,用于监听实时扫描结果
static StreamSubscription<List<ScanResult>>? _scanSubscription;
/// 开始扫描BLE设备
/// [timeout] 扫描超时时间,默认10秒
/// [serviceUuids] 指定要扫描的服务UUID列表,空列表表示扫描所有设备
/// [allowDuplicates] 是否允许重复设备出现在结果中
static Future<void> startScan({
Duration timeout = const Duration(seconds: 10),
List<String> serviceUuids = const [],
bool allowDuplicates = false,
}) async {
try {
// 检查蓝牙权限是否已授予
if (!await BluetoothPermissionManager.checkBluetoothPermissions()) {
throw Exception('蓝牙权限未授予');
}
// 检查蓝牙是否可用(支持且已开启)
if (!await BluetoothStateManager.isBluetoothAvailable()) {
throw Exception('蓝牙不可用');
}
// 停止之前可能正在进行的扫描,避免冲突
await stopScan();
// 清空之前的扫描结果,开始新的扫描
_scanResults.clear();
// 启动BLE设备扫描
await FlutterBluePlus.startScan(
timeout: timeout, // 扫描超时时间
withServices: serviceUuids.map((uuid) => Guid(uuid)).toList(), // 指定服务UUID
allowDuplicates: allowDuplicates, // 是否允许重复设备
);
// 监听扫描结果流,实时处理发现的设备
_scanSubscription = FlutterBluePlus.scanResults.listen(
(results) {
// 遍历扫描结果
for (ScanResult result in results) {
// 避免重复添加相同设备到结果列表
if (!_scanResults.contains(result.device)) {
_scanResults.add(result.device);
}
}
},
);
} catch (e) {
// 捕获并重新抛出扫描过程中的异常
print('扫描失败: $e');
rethrow;
}
}
/// 停止BLE设备扫描
/// 清理扫描相关的资源和订阅
static Future<void> stopScan() async {
// 停止底层的蓝牙扫描
await FlutterBluePlus.stopScan();
// 取消扫描结果订阅,释放资源
await _scanSubscription?.cancel();
_scanSubscription = null;
}
/// 获取当前扫描结果的副本
/// 返回设备列表的副本,避免外部直接修改内部列表
static List<BluetoothDevice> get scanResults => List.from(_scanResults);
/// 获取扫描结果的实时流
/// 可以监听这个流来实时获取扫描到的设备
static Stream<List<ScanResult>> get scanResultsStream =>
FlutterBluePlus.scanResults;
/// 获取扫描状态的实时流
/// 可以监听这个流来了解当前是否正在扫描
static Stream<bool> get isScanningStream =>
FlutterBluePlus.isScanning;
}
5.3 BLE连接状态管理

/// BLE设备连接管理器
/// 负责管理BLE设备的连接、断开和状态监控
class BLEConnectionManager {
/// 存储已连接的设备,key为设备ID,value为设备对象
static final Map<String, BluetoothDevice> _connectedDevices = {};
/// 存储连接状态订阅,用于监听设备连接状态变化
static final Map<String, StreamSubscription> _connectionSubscriptions = {};
/// 连接BLE设备
/// [device] 要连接的蓝牙设备
/// [timeout] 连接超时时间,默认15秒
/// [autoConnect] 是否自动重连,默认false
/// 返回true表示连接成功,false表示连接失败
static Future<bool> connectDevice(
BluetoothDevice device, {
Duration timeout = const Duration(seconds: 15),
bool autoConnect = false,
}) async {
try {
// 检查设备是否已经连接,避免重复连接
if (await device.connectionState.first == BluetoothConnectionState.connected) {
_connectedDevices[device.remoteId.str] = device;
return true;
}
// 监听设备连接状态变化,实时更新连接状态
_connectionSubscriptions[device.remoteId.str] =
device.connectionState.listen((state) {
if (state == BluetoothConnectionState.connected) {
// 设备连接成功,添加到已连接设备列表
_connectedDevices[device.remoteId.str] = device;
} else if (state == BluetoothConnectionState.disconnected) {
// 设备断开连接,清理相关资源
_connectedDevices.remove(device.remoteId.str);
_connectionSubscriptions[device.remoteId.str]?.cancel();
_connectionSubscriptions.remove(device.remoteId.str);
}
});
// 发起设备连接请求
await device.connect(
timeout: timeout, // 连接超时时间
autoConnect: autoConnect, // 是否启用自动重连
);
return true;
} catch (e) {
// 捕获连接过程中的异常(超时、设备不可达等)
print('连接设备失败: $e');
return false;
}
}
/// 断开BLE设备连接
/// [device] 要断开的蓝牙设备
static Future<void> disconnectDevice(BluetoothDevice device) async {
try {
// 发起断开连接请求
await device.disconnect();
// 清理设备相关的资源和订阅
_connectedDevices.remove(device.remoteId.str);
_connectionSubscriptions[device.remoteId.str]?.cancel();
_connectionSubscriptions.remove(device.remoteId.str);
} catch (e) {
// 捕获断开连接时的异常
print('断开连接失败: $e');
}
}
/// 断开所有已连接的设备
/// 通常在应用退出或重置时调用
static Future<void> disconnectAllDevices() async {
// 创建设备列表副本,避免在遍历过程中修改原列表
final devices = List<BluetoothDevice>.from(_connectedDevices.values);
// 逐个断开所有设备
for (final device in devices) {
await disconnectDevice(device);
}
}
/// 获取当前已连接的设备列表
/// 返回设备列表的副本,避免外部直接修改内部列表
static List<BluetoothDevice> get connectedDevices =>
List.from(_connectedDevices.values);
/// 检查指定设备是否已连接
/// [device] 要检查的蓝牙设备
/// 返回true表示设备已连接,false表示未连接
static bool isDeviceConnected(BluetoothDevice device) {
return _connectedDevices.containsKey(device.remoteId.str);
}
/// 获取设备连接状态的实时流
/// [device] 要监听的蓝牙设备
/// 返回连接状态流,可以监听连接、断开等状态变化
static Stream<BluetoothConnectionState> getConnectionStateStream(
BluetoothDevice device
) {
return device.connectionState;
}
}
5.4 BLE服务和特征发现
/// BLE服务管理器
/// 负责发现和管理BLE设备的服务(Service)和特征(Characteristic)
class BLEServiceManager {
/// 发现BLE设备的所有服务和特征
/// [device] 已连接的BLE设备
/// 返回设备提供的所有服务列表
static Future<List<BluetoothService>> discoverServices(
BluetoothDevice device
) async {
try {
// 确保设备处于已连接状态,只有连接状态下才能发现服务
final connectionState = await device.connectionState.first;
if (connectionState != BluetoothConnectionState.connected) {
throw Exception('设备未连接');
}
// 发起服务发现请求,获取设备提供的所有服务
final services = await device.discoverServices();
// 遍历并打印发现的服务和特征信息,便于调试
for (final service in services) {
print('发现服务: ${service.uuid}');
// 遍历服务下的所有特征
for (final characteristic in service.characteristics) {
print(' 特征: ${characteristic.uuid}');
print(' 属性: ${_getCharacteristicProperties(characteristic)}');
}
}
return services;
} catch (e) {
// 捕获服务发现过程中的异常
print('发现服务失败: $e');
rethrow;
}
}
/// 根据UUID查找指定的服务
/// [services] 服务列表
/// [serviceUuid] 要查找的服务UUID字符串
/// 返回匹配的服务对象,如果未找到则返回null
static BluetoothService? findService(
List<BluetoothService> services,
String serviceUuid
) {
try {
// 在服务列表中查找匹配UUID的服务(忽略大小写)
return services.firstWhere(
(service) => service.uuid.toString().toLowerCase() ==
serviceUuid.toLowerCase()
);
} catch (e) {
// 如果没有找到匹配的服务,返回null
return null;
}
}
/// 根据UUID查找服务中的指定特征
/// [service] 要搜索的服务对象
/// [characteristicUuid] 要查找的特征UUID字符串
/// 返回匹配的特征对象,如果未找到则返回null
static BluetoothCharacteristic? findCharacteristic(
BluetoothService service,
String characteristicUuid
) {
try {
// 在特征列表中查找匹配UUID的特征(忽略大小写)
return service.characteristics.firstWhere(
(char) => char.uuid.toString().toLowerCase() ==
characteristicUuid.toLowerCase()
);
} catch (e) {
// 如果没有找到匹配的特征,返回null
return null;
}
}
/// 获取特征的属性描述列表
/// [characteristic] 要分析的特征对象
/// 返回特征支持的操作类型列表(如READ、WRITE、NOTIFY等)
static List<String> _getCharacteristicProperties(
BluetoothCharacteristic characteristic
) {
final properties = <String>[];
// 检查特征支持的各种操作类型
if (characteristic.properties.read) properties.add('READ'); // 支持读取
if (characteristic.properties.write) properties.add('WRITE'); // 支持写入(需要响应)
if (characteristic.properties.writeWithoutResponse) { // 支持写入(无需响应)
properties.add('WRITE_NO_RESPONSE');
}
if (characteristic.properties.notify) properties.add('NOTIFY'); // 支持通知
if (characteristic.properties.indicate) properties.add('INDICATE'); // 支持指示
return properties;
}
}
5.5 BLE数据读写操作

/// BLE数据管理器
/// 负责处理BLE特征的数据读取、写入和通知订阅操作
class BLEDataManager {
/// 读取BLE特征的当前值
/// [characteristic] 要读取的特征对象
/// 返回特征的字节数据列表
static Future<List<int>> readCharacteristic(
BluetoothCharacteristic characteristic
) async {
try {
// 检查特征是否支持读取操作
if (!characteristic.properties.read) {
throw Exception('特征不支持读取操作');
}
// 执行读取操作,获取特征的当前值
final value = await characteristic.read();
// 以十六进制格式打印读取的数据,便于调试
print('读取数据: ${value.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');
return value;
} catch (e) {
// 捕获读取过程中的异常
print('读取特征失败: $e');
rethrow;
}
}
/// 向BLE特征写入数据
/// [characteristic] 要写入的特征对象
/// [value] 要写入的字节数据列表
/// [withoutResponse] 是否使用无响应写入模式,默认false
static Future<void> writeCharacteristic(
BluetoothCharacteristic characteristic,
List<int> value, {
bool withoutResponse = false,
}) async {
try {
// 根据写入模式检查特征是否支持对应的写入操作
if (withoutResponse && !characteristic.properties.writeWithoutResponse) {
throw Exception('特征不支持无响应写入');
} else if (!withoutResponse && !characteristic.properties.write) {
throw Exception('特征不支持写入操作');
}
// 执行写入操作
await characteristic.write(
value, // 要写入的数据
withoutResponse: withoutResponse, // 写入模式:true=无需响应,false=需要响应
);
// 以十六进制格式打印写入的数据,便于调试
print('写入数据: ${value.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');
} catch (e) {
// 捕获写入过程中的异常
print('写入特征失败: $e');
rethrow;
}
}
/// 订阅BLE特征的通知或指示
/// [characteristic] 要订阅的特征对象
/// [onData] 接收到数据时的回调函数
/// 返回订阅对象,用于后续取消订阅
static Future<StreamSubscription<List<int>>> subscribeToNotifications(
BluetoothCharacteristic characteristic,
Function(List<int>) onData,
) async {
try {
// 检查特征是否支持通知或指示功能
if (!characteristic.properties.notify &&
!characteristic.properties.indicate) {
throw Exception('特征不支持通知');
}
// 启用特征的通知功能
await characteristic.setNotifyValue(true);
// 监听特征值变化的数据流
final subscription = characteristic.lastValueStream.listen((value) {
// 以十六进制格式打印接收到的通知数据
print('收到通知: ${value.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');
// 调用用户提供的数据处理回调函数
onData(value);
});
return subscription;
} catch (e) {
// 捕获订阅过程中的异常
print('订阅通知失败: $e');
rethrow;
}
}
/// 取消BLE特征的通知订阅
/// [characteristic] 要取消订阅的特征对象
/// [subscription] 之前创建的订阅对象
static Future<void> unsubscribeFromNotifications(
BluetoothCharacteristic characteristic,
StreamSubscription<List<int>> subscription,
) async {
try {
// 禁用特征的通知功能
await characteristic.setNotifyValue(false);
// 取消数据流订阅,释放资源
await subscription.cancel();
} catch (e) {
print('取消订阅失败: $e');
}
}
// 数据格式转换工具
/// 将字节数组转换为十六进制字符串
/// [bytes] 要转换的字节数组
/// 返回格式化的十六进制字符串,如 "01 A2 FF"
static String bytesToHex(List<int> bytes) {
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ');
}
/// 将十六进制字符串转换为字节数组
/// [hex] 十六进制字符串,支持空格分隔和0x前缀
/// 返回对应的字节数组
static List<int> hexToBytes(String hex) {
// 清理输入字符串,移除空格和0x前缀
final cleanHex = hex.replaceAll(' ', '').replaceAll('0x', '');
final bytes = <int>[];
// 每两个字符解析为一个字节
for (int i = 0; i < cleanHex.length; i += 2) {
final hexByte = cleanHex.substring(i, i + 2);
bytes.add(int.parse(hexByte, radix: 16));
}
return bytes;
}
/// 将字节数组转换为字符串
/// [bytes] 要转换的字节数组
/// 返回对应的UTF-8字符串
static String bytesToString(List<int> bytes) {
return String.fromCharCodes(bytes);
}
/// 将字符串转换为字节数组
/// [text] 要转换的字符串
/// 返回对应的UTF-8字节数组
static List<int> stringToBytes(String text) {
return text.codeUnits;
}
}
6. 经典蓝牙操作
6.1 经典蓝牙串口通信
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
/// 经典蓝牙管理器类
/// 提供经典蓝牙设备的连接、数据传输等功能
class ClassicBluetoothManager {
// 当前蓝牙连接对象
static BluetoothConnection? _connection;
// 数据接收订阅对象
static StreamSubscription<Uint8List>? _dataSubscription;
/// 获取已配对的蓝牙设备列表
/// 返回系统中已配对的经典蓝牙设备
static Future<List<BluetoothDevice>> getBondedDevices() async {
try {
// 从系统获取已配对设备列表
final devices = await FlutterBluetoothSerial.instance.getBondedDevices();
return devices;
} catch (e) {
print('获取已配对设备失败: $e');
return [];
}
}
/// 连接到指定的蓝牙设备
/// [device] 要连接的蓝牙设备对象
/// 返回连接是否成功
static Future<bool> connectToDevice(BluetoothDevice device) async {
try {
// 通过设备地址建立蓝牙连接
_connection = await BluetoothConnection.toAddress(device.address);
print('已连接到设备: ${device.name}');
return true;
} catch (e) {
print('连接设备失败: $e');
return false;
}
}
/// 断开当前蓝牙连接
/// 清理所有相关资源
static Future<void> disconnect() async {
try {
// 取消数据接收订阅
await _dataSubscription?.cancel();
// 关闭蓝牙连接
await _connection?.close();
// 清空连接引用
_connection = null;
_dataSubscription = null;
print('已断开连接');
} catch (e) {
print('断开连接失败: $e');
}
}
/// 向连接的设备发送数据
/// [data] 要发送的字符串数据
/// 抛出异常如果设备未连接或发送失败
static Future<void> sendData(String data) async {
// 检查连接状态
if (_connection == null || !_connection!.isConnected) {
throw Exception('设备未连接');
}
try {
// 将字符串转换为字节数组并发送
_connection!.output.add(Uint8List.fromList(data.codeUnits));
// 等待数据发送完成
await _connection!.output.allSent;
print('发送数据: $data');
} catch (e) {
print('发送数据失败: $e');
rethrow;
}
}
/// 开始监听从设备接收的数据
/// [onDataReceived] 数据接收回调函数
/// 抛出异常如果设备未连接
static void startListening(Function(String) onDataReceived) {
// 检查连接状态
if (_connection == null || !_connection!.isConnected) {
throw Exception('设备未连接');
}
// 订阅输入数据流
_dataSubscription = _connection!.input!.listen(
(Uint8List data) {
// 将接收到的字节数据转换为字符串
final receivedData = String.fromCharCodes(data);
print('接收数据: $receivedData');
// 调用用户提供的数据处理回调
onDataReceived(receivedData);
},
onError: (error) {
print('数据接收错误: $error');
},
onDone: () {
print('数据流结束');
},
);
}
/// 停止监听数据接收
/// 取消数据流订阅
static Future<void> stopListening() async {
await _dataSubscription?.cancel();
_dataSubscription = null;
}
/// 检查当前连接状态
/// 返回true如果设备已连接,否则返回false
static bool get isConnected =>
_connection != null && _connection!.isConnected;
}
7. 实战项目示例
7.1 Flutter蓝牙应用架构图

7.2 蓝牙数据流向图

7.3 设备连接生命周期图

7.4 完整的BLE设备管理应用
/// 蓝牙设备管理主界面
/// 提供设备扫描、连接、数据交互等完整功能
class BluetoothDeviceScreen extends StatefulWidget {
@override
_BluetoothDeviceScreenState createState() => _BluetoothDeviceScreenState();
}
class _BluetoothDeviceScreenState extends State<BluetoothDeviceScreen> {
// 扫描结果列表
List<ScanResult> _scanResults = [];
// 当前连接的设备
BluetoothDevice? _connectedDevice;
// 设备服务列表
List<BluetoothService> _services = [];
// 扫描状态标志
bool _isScanning = false;
// 连接状态标志
bool _isConnected = false;
@override
void initState() {
super.initState();
// 初始化蓝牙功能
_initializeBluetooth();
}
/// 初始化蓝牙功能
/// 包括权限检查、蓝牙状态检查和开始扫描
Future<void> _initializeBluetooth() async {
// 请求蓝牙相关权限
final hasPermission = await BluetoothPermissionManager.requestBluetoothPermissions();
if (!hasPermission) {
_showErrorDialog('蓝牙权限未授予');
return;
}
// 检查蓝牙是否可用
final isAvailable = await BluetoothStateManager.isBluetoothAvailable();
if (!isAvailable) {
_showErrorDialog('蓝牙不可用');
return;
}
// 开始扫描设备
_startScan();
}
/// 开始扫描BLE设备
/// 设置扫描超时时间并监听扫描结果
Future<void> _startScan() async {
setState(() {
_isScanning = true;
_scanResults.clear();
});
try {
// 开始扫描,设置10秒超时
await BLEScanner.startScan(timeout: Duration(seconds: 10));
// 监听扫描结果流,实时更新设备列表
BLEScanner.scanResultsStream.listen((results) {
setState(() {
_scanResults = results;
});
});
// 监听扫描状态流,更新扫描状态指示器
BLEScanner.isScanningStream.listen((isScanning) {
setState(() {
_isScanning = isScanning;
});
});
} catch (e) {
_showErrorDialog('扫描失败: $e');
setState(() {
_isScanning = false;
});
}
}
/// 连接到指定的BLE设备
/// [device] 要连接的蓝牙设备对象
Future<void> _connectToDevice(BluetoothDevice device) async {
try {
// 尝试连接设备
final success = await BLEConnectionManager.connectDevice(device);
if (success) {
setState(() {
_connectedDevice = device;
_isConnected = true;
});
// 连接成功后发现设备服务
final services = await BLEServiceManager.discoverServices(device);
setState(() {
_services = services;
});
_showSuccessDialog('设备连接成功');
} else {
_showErrorDialog('设备连接失败');
}
} catch (e) {
_showErrorDialog('连接错误: $e');
}
}
/// 断开当前连接的设备
/// 清理连接状态和服务信息
Future<void> _disconnectDevice() async {
if (_connectedDevice != null) {
// 断开设备连接
await BLEConnectionManager.disconnectDevice(_connectedDevice!);
setState(() {
_connectedDevice = null;
_isConnected = false;
_services.clear();
});
}
}
/// 构建主界面UI
/// 根据连接状态显示不同的界面内容
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('蓝牙设备管理'),
actions: [
// 根据连接状态显示不同的操作按钮
if (_isConnected)
IconButton(
icon: Icon(Icons.bluetooth_disabled),
onPressed: _disconnectDevice,
)
else
IconButton(
icon: Icon(_isScanning ? Icons.stop : Icons.refresh),
onPressed: _isScanning ? BLEScanner.stopScan : _startScan,
),
],
),
body: BluetoothStateWidget(
child: _buildBody(),
),
);
}
/// 构建主体内容
/// 根据连接状态显示设备详情或扫描结果
Widget _buildBody() {
if (_isConnected && _connectedDevice != null) {
return _buildDeviceDetails();
} else {
return _buildScanResults();
}
}
/// 构建设备扫描结果列表
/// 显示扫描进度和发现的设备
Widget _buildScanResults() {
return Column(
children: [
// 扫描进度指示器
if (_isScanning)
LinearProgressIndicator(),
Padding(
padding: EdgeInsets.all(16),
child: Text(
_isScanning ? '正在扫描设备...' : '扫描完成',
style: Theme.of(context).textTheme.titleMedium,
),
),
// 设备列表
Expanded(
child: ListView.builder(
itemCount: _scanResults.length,
itemBuilder: (context, index) {
final result = _scanResults[index];
return _buildDeviceListTile(result);
},
),
),
],
);
}
/// 构建设备列表项
/// [result] 扫描结果对象,包含设备信息和信号强度
Widget _buildDeviceListTile(ScanResult result) {
final device = result.device;
final rssi = result.rssi;
return ListTile(
leading: Icon(
Icons.bluetooth,
// 根据信号强度设置图标颜色
color: _getSignalColor(rssi),
),
title: Text(
// 显示设备名称,如果为空则显示"未知设备"
device.platformName.isNotEmpty ? device.platformName : '未知设备',
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('MAC: ${device.remoteId}'),
Text('信号强度: ${rssi}dBm'),
],
),
trailing: ElevatedButton(
onPressed: () => _connectToDevice(device),
child: Text('连接'),
),
);
}
/// 构建已连接设备的详情界面
/// 显示设备信息和可用服务列表
Widget _buildDeviceDetails() {
return Column(
children: [
// 设备信息卡片
Card(
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'已连接设备',
style: Theme.of(context).textTheme.titleLarge,
),
SizedBox(height: 8),
Text('名称: ${_connectedDevice!.platformName}'),
Text('MAC: ${_connectedDevice!.remoteId}'),
],
),
),
),
// 服务列表
Expanded(
child: ListView.builder(
itemCount: _services.length,
itemBuilder: (context, index) {
final service = _services[index];
return _buildServiceTile(service);
},
),
),
],
);
}
/// 构建服务展开项
/// [service] 蓝牙服务对象,包含特征列表
Widget _buildServiceTile(BluetoothService service) {
return ExpansionTile(
title: Text('服务: ${service.uuid}'),
// 展开显示该服务下的所有特征
children: service.characteristics.map((characteristic) {
return _buildCharacteristicTile(characteristic);
}).toList(),
);
}
/// 构建特征列表项
/// [characteristic] 蓝牙特征对象,包含读写通知等属性
Widget _buildCharacteristicTile(BluetoothCharacteristic characteristic) {
return ListTile(
title: Text('特征: ${characteristic.uuid}'),
subtitle: Text(
// 显示特征支持的操作属性
'属性: ${BLEServiceManager._getCharacteristicProperties(characteristic).join(', ')}'
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
// 如果支持读取,显示读取按钮
if (characteristic.properties.read)
IconButton(
icon: Icon(Icons.download),
onPressed: () => _readCharacteristic(characteristic),
),
// 如果支持写入,显示写入按钮
if (characteristic.properties.write)
IconButton(
icon: Icon(Icons.upload),
onPressed: () => _writeCharacteristic(characteristic),
),
// 如果支持通知,显示订阅按钮
if (characteristic.properties.notify)
IconButton(
icon: Icon(Icons.notifications),
onPressed: () => _subscribeToNotifications(characteristic),
),
],
),
);
}
/// 读取特征值数据
/// [characteristic] 要读取的蓝牙特征对象
Future<void> _readCharacteristic(BluetoothCharacteristic characteristic) async {
try {
// 读取特征值并转换为十六进制显示
final value = await BLEDataManager.readCharacteristic(characteristic);
_showDataDialog('读取数据', BLEDataManager.bytesToHex(value));
} catch (e) {
_showErrorDialog('读取失败: $e');
}
}
/// 向特征写入数据
/// [characteristic] 要写入的蓝牙特征对象
Future<void> _writeCharacteristic(BluetoothCharacteristic characteristic) async {
final controller = TextEditingController();
// 显示数据输入对话框
final result = await showDialog<String>(
context: context,
builder: (context) => AlertDialog(
title: Text('写入数据'),
content: TextField(
controller: controller,
decoration: InputDecoration(
hintText: '输入要写入的数据(十六进制)',
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, controller.text),
child: Text('写入'),
),
],
),
);
// 如果用户输入了数据,执行写入操作
if (result != null && result.isNotEmpty) {
try {
// 将十六进制字符串转换为字节数组
final bytes = BLEDataManager.hexToBytes(result);
await BLEDataManager.writeCharacteristic(characteristic, bytes);
_showSuccessDialog('数据写入成功');
} catch (e) {
_showErrorDialog('写入失败: $e');
}
}
}
/// 订阅特征通知
/// [characteristic] 要订阅的蓝牙特征对象
Future<void> _subscribeToNotifications(BluetoothCharacteristic characteristic) async {
try {
// 订阅通知并设置数据接收回调
await BLEDataManager.subscribeToNotifications(
characteristic,
(data) {
// 收到通知时显示数据
_showDataDialog('收到通知', BLEDataManager.bytesToHex(data));
},
);
_showSuccessDialog('已订阅通知');
} catch (e) {
_showErrorDialog('订阅失败: $e');
}
}
/// 根据信号强度返回对应的颜色
/// [rssi] 信号强度值(dBm)
/// 返回绿色(强)、橙色(中)或红色(弱)
Color _getSignalColor(int rssi) {
if (rssi >= -50) return Colors.green; // 强信号
if (rssi >= -70) return Colors.orange; // 中等信号
return Colors.red; // 弱信号
}
/// 显示错误对话框
/// [message] 要显示的错误信息
void _showErrorDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('错误'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('确定'),
),
],
),
);
}
/// 显示成功对话框
/// [message] 要显示的成功信息
void _showSuccessDialog(String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('成功'),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('确定'),
),
],
),
);
}
/// 显示数据对话框
/// [title] 对话框标题
/// [data] 要显示的数据内容(可选择复制)
void _showDataDialog(String title, String data) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
// 使用SelectableText允许用户选择和复制数据
content: SelectableText(data),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('确定'),
),
],
),
);
}
/// 页面销毁时清理资源
/// 停止扫描并断开所有连接
@override
void dispose() {
// 停止设备扫描
BLEScanner.stopScan();
// 断开所有设备连接
BLEConnectionManager.disconnectAllDevices();
super.dispose();
}
}
8. 性能优化与最佳实践
8.1 蓝牙性能优化策略图

8.2 错误处理流程图

/// 常见错误处理
/// 蓝牙错误处理工具类
/// 提供错误信息解析和重试机制
class BluetoothErrorHandler {
/// 将系统错误转换为用户友好的错误信息
/// [error] 原始错误对象
/// 返回本地化的错误描述
static String getErrorMessage(dynamic error) {
final errorStr = error.toString().toLowerCase();
// 权限相关错误
if (errorStr.contains('permission')) {
return '权限不足,请检查蓝牙权限设置';
}
// 蓝牙未开启错误
else if (errorStr.contains('bluetooth is off')) {
return '蓝牙未开启,请开启蓝牙后重试';
}
// 设备未找到错误
else if (errorStr.contains('device not found')) {
return '设备未找到,请确认设备在范围内';
}
// 连接失败错误
else if (errorStr.contains('connection failed')) {
return '连接失败,请重试或检查设备状态';
}
// 操作超时错误
else if (errorStr.contains('timeout')) {
return '操作超时,请检查网络连接';
}
// 特征值未找到错误
else if (errorStr.contains('characteristic not found')) {
return '特征值未找到,请确认设备支持该功能';
}
// 写入失败错误
else if (errorStr.contains('write failed')) {
return '写入失败,请检查特征值权限';
}
// 其他未知错误
else {
return '未知错误: $error';
}
}
/// 带重试机制的操作执行
/// [operation] 要执行的异步操作
/// [maxRetries] 最大重试次数,默认3次
/// [delay] 重试间隔时间,默认1秒
/// 返回操作结果,失败时返回null或抛出异常
static Future<T?> withRetry<T>(
Future<T> Function() operation, {
int maxRetries = 3,
Duration delay = const Duration(seconds: 1),
}) async {
// 执行重试循环
for (int i = 0; i < maxRetries; i++) {
try {
// 尝试执行操作
return await operation();
} catch (e) {
// 如果是最后一次重试,直接抛出异常
if (i == maxRetries - 1) {
rethrow;
}
// 记录重试信息并等待
print('操作失败,${delay.inSeconds}秒后重试 (${i + 1}/$maxRetries): $e');
await Future.delayed(delay);
}
}
return null;
}
}
8.3 连接管理优化
/// 优化的BLE管理器
/// 提供智能扫描、连接池管理等性能优化功能
class OptimizedBLEManager {
// 设备缓存,避免重复创建设备对象
static final Map<String, BluetoothDevice> _deviceCache = {};
// 扫描时间记录,用于控制扫描频率
static final Map<String, DateTime> _lastScanTime = {};
// 扫描冷却时间,防止频繁扫描消耗电量
static const Duration _scanCooldown = Duration(seconds: 5);
/// 智能扫描功能
/// 避免频繁扫描,减少电量消耗和系统负担
static Future<void> smartScan() async {
final now = DateTime.now();
final lastScan = _lastScanTime['last'];
// 检查是否在冷却期内
if (lastScan != null && now.difference(lastScan) < _scanCooldown) {
print('扫描冷却中,跳过本次扫描');
return;
}
// 更新最后扫描时间并开始扫描
_lastScanTime['last'] = now;
await BLEScanner.startScan();
}
/// 连接池管理 - 获取或连接设备
/// [deviceId] 设备唯一标识符
/// 返回连接的设备对象,如果连接失败返回null
static Future<BluetoothDevice?> getOrConnectDevice(String deviceId) async {
// 检查设备缓存
if (_deviceCache.containsKey(deviceId)) {
final device = _deviceCache[deviceId]!;
// 检查当前连接状态
final state = await device.connectionState.first;
if (state == BluetoothConnectionState.connected) {
return device;
}
}
// 尝试重新连接缓存中的设备
final device = _deviceCache[deviceId];
if (device != null) {
final success = await BLEConnectionManager.connectDevice(device);
return success ? device : null;
}
return null;
}
/// 批量读取特征值优化
/// [characteristics] 要读取的特征列表
/// 返回特征UUID到数据值的映射
static Future<Map<String, List<int>>> batchReadCharacteristics(
List<BluetoothCharacteristic> characteristics
) async {
final results = <String, List<int>>{};
// 限制并发读取数量,避免系统过载
const maxConcurrent = 3;
final semaphore = Semaphore(maxConcurrent);
// 创建并发读取任务
final futures = characteristics.map((char) async {
// 获取信号量许可
await semaphore.acquire();
try {
// 执行特征值读取
final value = await BLEDataManager.readCharacteristic(char);
results[char.uuid.toString()] = value;
} finally {
// 释放信号量许可
semaphore.release();
}
});
// 等待所有读取任务完成
await Future.wait(futures);
return results;
}
}
/// 信号量实现类
/// 用于控制并发操作的数量,防止系统资源过载
class Semaphore {
// 最大并发数
final int maxCount;
// 当前可用许可数
int _currentCount;
// 等待队列
final Queue<Completer<void>> _waitQueue = Queue<Completer<void>>();
/// 构造函数
/// [maxCount] 最大并发数
Semaphore(this.maxCount) : _currentCount = maxCount;
/// 获取信号量许可
/// 如果有可用许可则立即返回,否则等待
Future<void> acquire() async {
if (_currentCount > 0) {
_currentCount--;
return;
}
// 没有可用许可,加入等待队列
final completer = Completer<void>();
_waitQueue.add(completer);
return completer.future;
}
/// 释放信号量许可
/// 如果有等待的任务则唤醒,否则增加可用许可数
void release() {
if (_waitQueue.isNotEmpty) {
// 唤醒等待队列中的第一个任务
final completer = _waitQueue.removeFirst();
completer.complete();
} else {
// 增加可用许可数
_currentCount++;
}
}
}
8.4 数据传输优化
/// 优化的数据传输类
/// 提供大数据分包传输、数据压缩等功能
class OptimizedDataTransfer {
/// 分包传输大数据
/// [characteristic] 目标特征
/// [data] 要传输的数据
/// [chunkSize] 每包大小,默认20字节(BLE标准MTU-3)
/// [delay] 包间延迟,防止数据丢失
static Future<void> sendLargeData(
BluetoothCharacteristic characteristic,
List<int> data, {
int chunkSize = 20,
Duration delay = const Duration(milliseconds: 10),
}) async {
// 按块大小分割数据
for (int i = 0; i < data.length; i += chunkSize) {
final end = math.min(i + chunkSize, data.length);
final chunk = data.sublist(i, end);
// 发送数据块,使用无响应模式提高效率
await BLEDataManager.writeCharacteristic(
characteristic,
chunk,
withoutResponse: true,
);
// 添加延迟避免接收端缓冲区溢出
if (i + chunkSize < data.length) {
await Future.delayed(delay);
}
}
}
/// 数据压缩传输(RLE压缩算法)
/// [data] 原始数据
/// 返回压缩后的数据
static List<int> compressData(List<int> data) {
// 简单的行程长度编码(RLE)压缩
final compressed = <int>[];
for (int i = 0; i < data.length; i++) {
int count = 1;
final value = data[i];
// 统计连续相同字节的数量
while (i + count < data.length &&
data[i + count] == value &&
count < 255) {
count++;
}
// 只有连续4个以上相同字节才进行压缩
if (count > 3) {
// 压缩格式:标记字节(0xFF) + 重复次数 + 原始值
compressed.addAll([0xFF, count, value]);
i += count - 1; // 跳过已处理的字节
} else {
// 不压缩,直接添加原始值
compressed.add(value);
}
}
return compressed;
}
/// 数据解压缩(RLE解压算法)
/// [compressed] 压缩后的数据
/// 返回解压后的原始数据
static List<int> decompressData(List<int> compressed) {
final decompressed = <int>[];
for (int i = 0; i < compressed.length; i++) {
// 检查是否为压缩标记
if (compressed[i] == 0xFF && i + 2 < compressed.length) {
// 解压缩:读取重复次数和原始值
final count = compressed[i + 1];
final value = compressed[i + 2];
decompressed.addAll(List.filled(count, value));
i += 2; // 跳过重复次数和值字节
} else {
// 非压缩数据,直接添加
decompressed.add(compressed[i]);
}
}
return decompressed;
}
}
9. 常见问题与解决方案
9.1 蓝牙问题诊断流程图

9.2 问题诊断工具
/// 蓝牙诊断工具类
/// 提供系统信息检查、权限验证、连接诊断等功能
class BluetoothDiagnostics {
/// 获取系统信息
/// 返回包含蓝牙支持、状态、权限等信息的映射
static Future<Map<String, dynamic>> getSystemInfo() async {
final info = <String, dynamic>{};
// 检查设备是否支持蓝牙
info['bluetoothSupported'] = await FlutterBluePlus.isSupported;
// 获取当前蓝牙适配器状态
info['bluetoothState'] = await FlutterBluePlus.adapterState.first;
// 检查所有相关权限状态
info['permissions'] = await _checkAllPermissions();
// 获取设备平台信息
final deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
info['platform'] = {
'type': 'Android',
'version': androidInfo.version.sdkInt,
'model': androidInfo.model,
'manufacturer': androidInfo.manufacturer,
};
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
info['platform'] = {
'type': 'iOS',
'version': iosInfo.systemVersion,
'model': iosInfo.model,
};
}
return info;
}
/// 检查所有蓝牙相关权限
/// 返回权限名称到授权状态的映射
static Future<Map<String, bool>> _checkAllPermissions() async {
final permissions = <String, bool>{};
if (Platform.isAndroid) {
// Android 12+ 需要的新蓝牙权限
permissions['bluetooth'] = await Permission.bluetooth.isGranted;
permissions['bluetoothScan'] = await Permission.bluetoothScan.isGranted;
permissions['bluetoothConnect'] = await Permission.bluetoothConnect.isGranted;
permissions['bluetoothAdvertise'] = await Permission.bluetoothAdvertise.isGranted;
// 位置权限(Android 10以下扫描蓝牙需要)
permissions['location'] = await Permission.locationWhenInUse.isGranted;
} else if (Platform.isIOS) {
// iOS只需要蓝牙权限
permissions['bluetooth'] = await Permission.bluetooth.isGranted;
}
return permissions;
}
/// 连接质量检测
/// [device] 要测试的蓝牙设备
/// 返回包含连接时间、服务发现时间、信号强度等信息的映射
static Future<Map<String, dynamic>> testConnectionQuality(
BluetoothDevice device
) async {
final results = <String, dynamic>{};
try {
// 测试设备连接时间
final connectStart = DateTime.now();
await BLEConnectionManager.connectDevice(device);
final connectTime = DateTime.now().difference(connectStart);
results['connectTime'] = connectTime.inMilliseconds;
// 测试服务发现时间
final discoverStart = DateTime.now();
final services = await BLEServiceManager.discoverServices(device);
final discoverTime = DateTime.now().difference(discoverStart);
results['discoverTime'] = discoverTime.inMilliseconds;
results['serviceCount'] = services.length;
// 读取信号强度(RSSI值)
try {
final rssi = await device.readRssi();
results['rssi'] = rssi;
} catch (e) {
// 某些设备可能不支持RSSI读取
results['rssi'] = null;
}
results['success'] = true;
} catch (e) {
results['success'] = false;
results['error'] = e.toString();
}
return results;
}
/// 生成完整的蓝牙诊断报告
/// 返回包含系统信息、权限状态等的格式化报告字符串
static Future<String> generateDiagnosticReport() async {
final systemInfo = await getSystemInfo();
final report = StringBuffer();
// 报告头部信息
report.writeln('=== Flutter蓝牙诊断报告 ===');
report.writeln('生成时间: ${DateTime.now()}');
report.writeln();
// 系统基本信息
report.writeln('系统信息:');
report.writeln(' 蓝牙支持: ${systemInfo['bluetoothSupported']}');
report.writeln(' 蓝牙状态: ${systemInfo['bluetoothState']}');
report.writeln(' 平台: ${systemInfo['platform']}');
report.writeln();
// 权限检查结果
report.writeln('权限状态:');
final permissions = systemInfo['permissions'] as Map<String, bool>;
permissions.forEach((key, value) {
report.writeln(' $key: $value');
});
return report.toString();
}
}
9.3 蓝牙调试工具使用流程

10. 进阶应用场景
10.1 蓝牙Mesh网络

10.2 设备固件升级(OTA)
/// BLE设备固件升级工具类
/// 提供Over-The-Air(OTA)固件更新功能
class BLEFirmwareUpdater {
// OTA服务和特征值UUID定义
static const String OTA_SERVICE_UUID = '0000180F-0000-1000-8000-00805F9B34FB';
static const String OTA_DATA_CHAR_UUID = '00002A19-0000-1000-8000-00805F9B34FB';
static const String OTA_CONTROL_CHAR_UUID = '00002A1A-0000-1000-8000-00805F9B34FB';
/// 执行固件升级
/// [device] 目标蓝牙设备
/// [firmwareData] 固件二进制数据
/// [onProgress] 进度回调函数,参数为0.0-1.0的进度值
/// 返回升级是否成功
static Future<bool> updateFirmware(
BluetoothDevice device,
List<int> firmwareData,
Function(double) onProgress,
) async {
try {
// 步骤1: 连接到目标设备
await BLEConnectionManager.connectDevice(device);
// 步骤2: 发现设备的OTA升级服务
final services = await BLEServiceManager.discoverServices(device);
final otaService = BLEServiceManager.findService(services, OTA_SERVICE_UUID);
if (otaService == null) {
throw Exception('设备不支持OTA升级');
}
// 步骤3: 获取OTA相关的特征值
final dataChar = BLEServiceManager.findCharacteristic(
otaService, OTA_DATA_CHAR_UUID
);
final controlChar = BLEServiceManager.findCharacteristic(
otaService, OTA_CONTROL_CHAR_UUID
);
if (dataChar == null || controlChar == null) {
throw Exception('OTA特征值未找到');
}
// 步骤4: 启动OTA升级流程
await _startOTAProcess(controlChar);
// 步骤5: 分包传输固件数据
const chunkSize = 244; // BLE MTU大小减去协议头部(248-4)
final totalChunks = (firmwareData.length / chunkSize).ceil();
// 逐包发送固件数据
for (int i = 0; i < totalChunks; i++) {
final start = i * chunkSize;
final end = math.min(start + chunkSize, firmwareData.length);
final chunk = firmwareData.sublist(start, end);
// 构造数据包:包序号(2字节)+ 数据内容
final packet = [i & 0xFF, (i >> 8) & 0xFF, ...chunk];
// 发送数据包(使用无响应模式提高传输效率)
await BLEDataManager.writeCharacteristic(
dataChar,
packet,
withoutResponse: true,
);
// 更新升级进度并通知调用者
final progress = (i + 1) / totalChunks;
onProgress(progress);
// 短暂延迟确保设备有足够时间处理数据
await Future.delayed(Duration(milliseconds: 10));
}
// 步骤6: 完成升级流程
await _finishOTAProcess(controlChar);
return true;
} catch (e) {
print('固件升级失败: $e');
return false;
}
}
/// 启动OTA升级流程
/// [controlChar] OTA控制特征值
static Future<void> _startOTAProcess(BluetoothCharacteristic controlChar) async {
// 发送开始升级命令(0x01)
await BLEDataManager.writeCharacteristic(controlChar, [0x01]);
}
/// 完成OTA升级流程
/// [controlChar] OTA控制特征值
static Future<void> _finishOTAProcess(BluetoothCharacteristic controlChar) async {
// 发送完成升级命令(0x02),设备将重启并应用新固件
await BLEDataManager.writeCharacteristic(controlChar, [0x02]);
}
}
10.3 蓝牙音频处理
/// 蓝牙音频设备管理类
/// 提供音频设备连接和配置功能
class BluetoothAudioManager {
// A2DP音频服务UUID
static const String AUDIO_SERVICE_UUID = '0000110B-0000-1000-8000-00805F9B34FB';
/// 连接蓝牙音频设备
/// [device] 目标音频设备
/// 返回连接是否成功
static Future<bool> connectAudioDevice(BluetoothDevice device) async {
try {
// 使用经典蓝牙协议连接音频设备(如耳机、音箱)
final success = await ClassicBluetoothManager.connectToDevice(device);
if (success) {
// 配置音频传输参数和编解码器
await _configureAudioProfile(device);
return true;
}
return false;
} catch (e) {
print('音频设备连接失败: $e');
return false;
}
}
/// 配置音频传输配置文件
/// [device] 已连接的音频设备
static Future<void> _configureAudioProfile(BluetoothDevice device) async {
// 配置A2DP(Advanced Audio Distribution Profile)音频配置文件
// 注意:此功能需要原生平台(Android/iOS)的支持
// 实际实现需要通过MethodChannel调用原生代码
}
}
10.4 核心要点
-
环境配置:正确配置权限和依赖
-
BLE vs 经典蓝牙:根据应用场景选择合适的技术
-
连接管理:实现稳定的设备连接和状态管理
-
数据传输:优化数据传输性能和可靠性
-
错误处理:完善的异常处理和用户反馈
-
性能优化:减少功耗和提升响应速度
10.5 最佳实践总结
-
始终检查权限和蓝牙状态
-
合理管理连接生命周期
-
实现重连机制和错误恢复
-
优化数据传输策略
-
提供良好的用户体验
-
进行充分的设备兼容性测试
10.6 进阶学习方向
-
蓝牙Mesh网络开发
-
设备固件升级(OTA)
-
蓝牙音频处理
-
自定义蓝牙协议设计
-
跨平台蓝牙插件开发
1201

被折叠的 条评论
为什么被折叠?



