官网解释
Bluetooth On & Off
Note: On iOS, a "This app would like to use Bluetooth" system dialogue appears on first call to any FlutterBluePlus method.
Dart
// first, check if bluetooth is supported by your hardware
// Note: The platform is initialized on the first call to any FlutterBluePlus method.
//(硬件是否支持蓝牙,现在的手机都支持)
if (await FlutterBluePlus.isSupported == false) {
print("Bluetooth not supported by this device");
return;
}
// handle bluetooth on & off
// note: for iOS the initial state is typically BluetoothAdapterState.unknown
// note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized
//(判断蓝牙开关的状态)
var subscription = FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) {
print(state);
if (state == BluetoothAdapterState.on) {
// usually start scanning, connecting, etc
} else {
// show an error to the user, etc
}
});
// turn on bluetooth ourself if we can
// for iOS, the user controls bluetooth enable/disable
//(Android 平台上开启蓝牙)
if (!kIsWeb && Platform.isAndroid) {
await FlutterBluePlus.turnOn();
}
// cancel to prevent duplicate listeners
//(取消监听)
subscription.cancel();
监听蓝牙开关状态
使用示例
Dart
StreamSubscription<BluetoothAdapterState>? bluetoothState;//监听蓝牙状态
bluetoothState = FlutterBluePlus.adapterState.listen((state){
if(state == BluetoothAdapterState.off) { //蓝牙未开启
Fluttertoast.showToast(msg: S.current.bluetoothClose,gravity: ToastGravity.CENTER); //给一个蓝牙关闭提示
}
});
//state == BluetoothAdapterState.on(蓝牙开启状态)
官网解释
Scan for devices
If your device is not found, see Common Problems.
Note: It is recommended to set scan filters to reduce main thread & platform channel usage.
Dart
// listen to scan results
// Note: `onScanResults` clears the results between scans. You should use
// `scanResults` if you want the current scan results *or* the results from the previous scan.
//(监听扫描结果)
var subscription = FlutterBluePlus.onScanResults.listen((results) {
if (results.isNotEmpty) {
ScanResult r = results.last; // the most recently found device
print('${r.device.remoteId}: "${r.advertisementData.advName}" found!');
}
},
onError: (e) => print(e),
);
// cleanup: cancel subscription when scanning stops
//(当蓝牙扫描停止时,自动取消指定的订阅)
FlutterBluePlus.cancelWhenScanComplete(subscription);
// Wait for Bluetooth enabled & permission granted
// In your real app you should use `FlutterBluePlus.adapterState.listen` to handle all states
//(异步等待操作:等待蓝牙适配器的开启,直到蓝牙状态变为已开启(on)为止)
await FlutterBluePlus.adapterState.where((val) => val == BluetoothAdapterState.on).first;
// Start scanning w/ timeout
// Optional: use `stopScan()` as an alternative to timeout
//(带过滤条件的扫描方法)
await FlutterBluePlus.startScan(
withServices:[Guid("180D")], // match any of the specified services
withNames:["Bluno"], // *or* any of the specified names
timeout: Duration(seconds:15));
// wait for scanning to stop
//(扫描状态监听器)
await FlutterBluePlus.isScanning.where((val) => val == false).first;
监听扫描结果
使用示例
Dart
StreamSubscription<List<ScanResult>> ? _scanStream; //声明扫描结果订阅流
//监听扫描结果
//FlutterBluePlus.scanResults:持续不断地推送扫描到的设备列表
_scanStream = FlutterBluePlus.scanResults.listen((scanList){
//遍历本次扫描到的所有设备
for(var result in scanList){
final item = isValid(result); //用isValid方法过滤有效设备
if(item == null) continue;
//创建设备实例
late Charge device;
device = Charge.from(item); //创建Charge类的实例
_callback?.call(device); //回调给调用者
}
});
//回调
//typedef ScanCallback = Function(Charge);
// ScanCallback? _callback;//扫描回调函数
//扫描方法需要传入扫描回调
//_callback = callback; 把传入的回调,复制给当前页面的回调函数
//_callback?.call(device); //把设备回调给调用者
带过滤条件的扫描方法
使用实例
Dart
FlutterBluePlus.startScan(
oneByOne: true, //逐个处理设备
timeout: const Duration(seconds: 300)).then((value) {
// _startScan(); 这个不进行注释,就是无限扫描
});
startScan里面的具体参数
Dart
static Future<void> startScan({
List<Guid> withServices = const [], //按服务过滤
List<String> withRemoteIds = const [], //按设备地址过滤
List<String> withNames = const [], //按设备名称过滤
List<String> withKeywords = const [], //按关键词过滤
List<MsdFilter> withMsd = const [], //按厂商数据过滤
List<ServiceDataFilter> withServiceData = const [], //按服务数据过滤
Duration? timeout, //扫描超时
Duration? removeIfGone, //移除消失的设备
bool continuousUpdates = false, //接受同一设备的广播更新
int continuousDivisor = 1, //更新频率除数
bool oneByOne = false, //逐个报告
bool androidLegacy = false, //Android传统扫描模式
AndroidScanMode androidScanMode = AndroidScanMode.lowLatency, //Android扫描模式
bool androidUsesFineLocation = false,//安卓精准定位权限
bool androidCheckLocationServices = true,//检查位置服务
List<Guid> webOptionalServices = const [],//Web平台服务过滤
扫描状态的监听
使用实例
Dart
StreamSubscription<bool>? _scanState; //扫描状态订阅
//监听扫描状态
if(_scanState == null) { //检查是否已经存在监听
_scanState = FlutterBluePlus.isScanning.listen((state) async {
// state = true → 正在扫描
// state = false → 扫描已停止
Log.d("scan state: $state"); //打印扫描状态
// 扫描停止且允许自动扫描时,重新开始
if(state == false && _autoScan) {
_startScan();// 重新开始扫描
}
});
} else {
//如果已经有监听器,直接开始扫描
_startScan();
}
官网解释
Connect to a device
Dart
// listen for disconnection
//(监听蓝牙设备的连接状态)
var subscription = device.connectionState.listen((BluetoothConnectionState state) async {
if (state == BluetoothConnectionState.disconnected) {
// 1. typically, start a periodic timer that tries to
// reconnect, or just call connect() again right now
// 2. you must always re-discover services after disconnection!
print("${device.disconnectReason?.code} ${device.disconnectReason?.description}");
}
});
// cleanup: cancel subscription when disconnected
// - [delayed] This option is only meant for `connectionState` subscriptions.
// When `true`, we cancel after a small delay. This ensures the `connectionState`
// listener receives the `disconnected` event.
// - [next] if true, the the stream will be canceled only on the *next* disconnection,
// not the current disconnection. This is useful if you setup your subscriptions
// before you connect.
//(当设备断开连接时,自动取消指定的订阅,防止内存泄漏和无效回调)
device.cancelWhenDisconnected(subscription, delayed:true, next:true);
// Connect to the device
//(发起连接)
await device.connect();
// Disconnect from device
//(发起断联)
await device.disconnect();
// cancel to prevent duplicate listeners
//(取消监听)
subscription.cancel();
蓝牙设备连接状态的监听
使用实例
Dart
typedef ConnectCallback = Function(bool);
ConnectCallback? _connectCallback;//连接结果回调 (布尔类型)
StreamSubscription<BluetoothConnectionState>? _stateStream; // 当前连接状态
var connectState = BluetoothConnectionState.disconnected;//当前连接状态,对外提供连接状态
//监听连接状态变化
_stateStream = device.connectionState.listen((event) async{
//disconnected, // 0 - 已断开
//connecting, // 1 - 连接中
//connected, // 2 - 已连接
//disconnecting, // 3 - 断开中
Log.d("$event");
_connecting = false; //连接操作已经完成
if(event == BluetoothConnectionState.connected){
//连接成功
if(_innerConnected){
_connectCallback?.call(true);//已连接,直接回调
return;
}
_innerConnected = true;
connectState = BluetoothConnectionState.connected;
///***发现服务(抽象方法 由子类实现)
//返回的是布尔值:表示服务发现是否成功
final result = await discoveryService();
_connectCallback?.call(result);//回调连接结果,发现了服务才可以通信
if(!result){
close(); //服务发现失败,关闭连接
}
}else{
// 连接失败或断开
_innerConnected = false;
_connectCallback?.call(false);
onDisconnect();
connectState = BluetoothConnectionState.disconnected;
}
});
蓝牙设备连接
使用实例
Dart
device.connect(mtu: 256, license: License.free);//开始连接
//connect的相关参数
//required License license, // 许可证(必填)
//Duration timeout = const Duration(seconds: 35), // 连接超时时间
//int? mtu = 512, // 最大传输单元
//bool autoConnect = false, // 是否自动连接
蓝牙设备断开连接
使用实例
Dart
device.disconnect(queue: false);
//disconnect的相关参数
//int timeout = 35, 超时时间(秒)
//bool queue = true,//是否排队等待
//int androidDelay = 2000,//Android延迟(毫秒)
官网解释
Auto Connect
Connects whenever your device is found.
Dart
// enable auto connect
// - note: autoConnect is incompatible with mtu argument, so you must call requestMtu yourself
//(自动回连)
await device.connect(autoConnect:true, mtu:null)
// wait until connection
// - when using autoConnect, connect() always returns immediately, so we must
// explicity listen to `device.connectionState` to know when connection occurs
//等待蓝牙设备连接成功--会阻塞代码执行,直到设备真正连接上
await device.connectionState.where((val) => val == BluetoothConnectionState.connected).first;
// disable auto connect
//(断连)
await device.disconnect()
Save Device
To save a device between app restarts, just write the remoteId to a file.
Now you can connect without needing to scan again, like so:
Dart
//(从文件读取保存的设备ID)
final String remoteId = await File('/remoteId.txt').readAsString();
//(根据ID创建蓝牙设备对象)
var device = BluetoothDevice.fromId(remoteId);
// AutoConnect is convenient because it does not "time out"
// even if the device is not available / turned off.
//(使用自动回连模式,连接设备)
await device.connect(autoConnect: true);
官网解释
MTU
On Android, we request an mtu of 512 by default during connection (see: connect function arguments).
On iOS & macOS, the mtu is negotiated automatically, typically 135 to 255.
Dart
//(监听蓝牙设备MTU变化)
final subscription = device.mtu.listen((int mtu) {
// iOS: initial value is always 23, but iOS will quickly negotiate a higher value
print("mtu $mtu");
});
// cleanup: cancel subscription when disconnected
//(在设备断开连接时自动取消订阅?)
device.cancelWhenDisconnected(subscription);
// You can also manually change the mtu yourself.
//(Android 平台上请求协商 MTU(最大传输单元))
if (!kIsWeb && Platform.isAndroid) {
await device.requestMtu(512);
}
协调MTU
使用实例
Dart
//监听MTU变化
StreamSubscription<int>? _mtuState;
_mtuState = device.mtu.listen((event) {
_command.getMtu(); //MTU变化时获取新的MTU值
});
Future<void> getMtu() async{
Log.d("get Mtu()");
_getDeviceInfo(DeviceInfoType.mtu);
}
Future<bool> _getDeviceInfo(int info) async {
return _write(cmd: CmdType.info, payload: [info,0]);
}
官网解释
Discover services
Dart
// Note: You must call discoverServices after every re-connection!
//(发现服务)
List<BluetoothService> services = await device.discoverServices();
services.forEach((service) {
// do something with service
});
发现服务
使用实例
Dart
static final Guid _serviceGuid = Guid("0000xxxx-0000-x000-xxxx-xxxxxxxxxxxxxxx");
final list = await device.discoverServices();//服务发现
for (var service in list) {
Log.d(service.uuid.toString());
if(service.uuid != _serviceGuid) {//服务过滤,只处理指定服务UUID
continue;
}
官网解释
Read Characteristics
Dart
// Reads all characteristics(特征读取)
//(获取服务下的所有特征)
var characteristics = service.characteristics;
//(遍历每个特征)
for(BluetoothCharacteristic c in characteristics) {
// (检查特征是否支持读取操作)
if (c.properties.read) {
// 读取特征值
List<int> value = await c.read();
print(value);
}
}
Write Characteristic
Dart
// Writes to a characteristic
//(特征写入)
await c.write([0x12, 0x34]);
allowLongWrite : To write large characteristics (up to 512 bytes) regardless of mtu, use allowLongWrite:
Dart
/// allowLongWrite should be used with caution.
/// 1. it can only be used *with* response to avoid data loss
/// 2. the peripheral device must support the 'long write' ble protocol.
/// 3. Interrupted transfers can leave the characteristic in a partially written state
/// 4. If the mtu is small, it is very very slow.
await c.write(data, allowLongWrite:true);
splitWrite : To write lots of data (unlimited), you can define the splitWrite function.
Dart
import 'dart:math';
// split write should be used with caution.
// 1. due to splitting, `characteristic.read()` will return partial data.
// 2. it can only be used *with* response to avoid data loss
// 3. The characteristic must be designed to support split data
//(自动分包写入)
extension splitWrite on BluetoothCharacteristic {
Future<void> splitWrite(List<int> value, {int timeout = 15}) async {
int chunk = min(device.mtuNow - 3, 512); // 3 bytes BLE overhead, 512 bytes max
for (int i = 0; i < value.length; i += chunk) {
List<int> subvalue = value.sublist(i, min(i + chunk, value.length));
await write(subvalue, withoutResponse:false, timeout: timeout);
}
}
}
Subscribe to a characteristic
If onValueReceived is never called, see Common Problems in the README.
Dart
//(订阅特征值数据流)
final subscription = characteristic.onValueReceived.listen((value) {
// onValueReceived is updated:
// - anytime read() is called
// - anytime a notification arrives (if subscribed)
});
// cleanup: cancel subscription when disconnected
//(设备断开时自动取消订阅)
device.cancelWhenDisconnected(subscription);
// subscribe
// Note: If a characteristic supports both **notifications** and **indications**,
// it will default to **notifications**. This matches how CoreBluetooth works on iOS.
//(启用通知)
await characteristic.setNotifyValue(true);
订阅特征值数据流
使用实例
Dart
//数据订阅管理
StreamSubscription<List<int>>? _valueState;
BluetoothCharacteristic? _txResponse,_tx,_rx;
//通信通道验证和设置
// TX: 专门发送,不关心接收
// RX: 专门接收,不关心发送
//txResponse: 接收响应(设备 → 手机)
if(_rx != null && _tx != null && _txResponse != null) {
Log.d("所有特征值都找到,设置通知...");
await setNotify(_rx!); ///***开启RX特征值的通知
Log.d("通知设置成功");
_valueState?.cancel();//取消旧监听
//创建新监听,接受数据
_valueState = _rx?.onValueReceived.listen((event) {
Log.d("rx: ${Log.listToHex(event)}");
if(event.isNotEmpty)_parse(event);//解析接收到的数据
});
Log.d("discoveryService 完成,返回 true");
} else {
Log.d("❌ 缺少必要的特征值");
Log.d(" _rx: ${_rx?.uuid}");
Log.d(" _tx: ${_tx?.uuid}");
return false;
}
Last Value Stream
lastValueStream is an alternative to onValueReceived. It emits a value any time the characteristic changes, including writes.
It is very convenient for simple characteristics that support both WRITE and READ (and/or NOTIFY). e.g. a "light switch toggle" characteristic.
Dart
//(特征值监听,订阅最后值流)
final subscription = characteristic.lastValueStream.listen((value) {
// lastValueStream` is updated:
// - anytime read() is called
// - anytime write() is called
// - anytime a notification arrives (if subscribed)
// - also when first listened to, it re-emits the last value for convenience.
});
// cleanup: cancel subscription when disconnected
// (设备断开时自动取消)
device.cancelWhenDisconnected(subscription);
// enable notifications
//(启动通知)
await characteristic.setNotifyValue(true);
Read and write descriptors
Dart
// Reads all descriptors
//(获取特征的所有描述符)
var descriptors = characteristic.descriptors;
//(遍历并读取每个描述符的值)
for(BluetoothDescriptor d in descriptors) {
List<int> value = await d.read(); // 读取描述符的值
print(value);
}
// Writes to a descriptor
//(写入描述符)
await d.write([0x12, 0x34])
Services Changed Characteristic
FlutterBluePlus automatically listens to the Services Changed Characteristic (0x2A05)
In FlutterBluePlus, we call it onServicesReset because you must re-discover services.
Dart
// - uses the GAP Services Changed characteristic (0x2A05)
// - you must call discoverServices() again
//(监听蓝牙服务重置事件)
device.onServicesReset.listen(() async {
print("Services Reset");
await device.discoverServices();//(重新发现服务)
});
Get Connected Devices
Get devices currently connected to your app.
Dart
//(获取当前已连接的所有蓝牙设备)
List<BluetoothDevice> devs = FlutterBluePlus.connectedDevices;
for (var d in devs) {
print(d);
}
Get System Devices
Get devices connected to the system by any app.
Note: before you can communicate, you must connect your app to these devices
Dart
// `withServices` required on iOS, ignored on android
List<Guid> withServices = [Guid("180F")]; // e.g. Battery Service
//(获取系统已配对的设备(根据服务过滤))
List<BluetoothDevice> devs = await FlutterBluePlus.systemDevices(withServices);
for (var d in devs) {
await d.connect(); // Must connect *our* app to the device 连接服务
await d.discoverServices(); //发现服务
}
Create Bond (Android Only)
Note: calling this is usually not necessary!! The platform will do it automatically.
However, you can force the popup to show sooner.
Dart
//(监听配对状态)
//notBonded, // 未配对
//bonding, // 配对中
//bonded, // 已配对
final bsSubscription = device.bondState.listen((value) {
print("$value prev:{$device.prevBondState}");
});
// cleanup: cancel subscription when disconnected
//(设备断开时自动取消监听)
device.cancelWhenDisconnected(bsSubscription);
// Force the bonding popup to show now (Android Only)
//(强制弹出系统配对对话框,让用户确认配对)
await device.createBond();
// remove bond
//(接触配对关系)
await device.removeBond();