flutter_blue_plus

官网解释

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();
相关推荐
2601_949970693 小时前
HumanizeDuration 跨端三方库仓颉适配全流程
前端·javascript·flutter
传奇开心果编程4 小时前
【Flutter踩坑排雷】第3课:const 误用坑|const 错误使用引发 UI 异常与性能损耗
flutter·移动开发·跨平台·dart·编程语言理论·编程框架·声明式ui
程序员老刘6 小时前
你的Flutter项目要不要紧急升级3.47.8?
flutter·客户端
Talyra428 小时前
不想开会员,我写了一个记账 App
flutter
2601_9495758613 小时前
JSON5 跨端三方库仓颉适配全流程
前端·javascript·flutter
法的空间13 小时前
AI 编程时代,为什么 Dart 分析会拖慢大型项目的开发
android·flutter·ios
恋猫de小郭15 小时前
Flutter 紧急发布 3.47.8 安全升级版本,建议都升级
android·前端·flutter
好的佩奇1 天前
Dart 之模式
android·flutter·dart
淡淡的香烟1 天前
Flutter实现多语言切换
android·flutter·1024程序员节
事圆则缓2 天前
从零搭建一套 Flutter 架构:先跑通数据流
flutter