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();
相关推荐
tangweiguo030519873 小时前
Flutter MVVM 完整实战:网络请求、状态管理、分页加载一网打尽
flutter
孤影过客4 小时前
Flutter优雅构建:从零打造开发级工作流
arm开发·数据库·flutter
p1gd0g5 小时前
flutter web 如何确保用户收到更新
flutter
GoCoding5 小时前
Flutter ngspice 插件
flutter
恋猫de小郭6 小时前
Android Studio Panda 2 ,支持 AI 用 Vibe Coding 创建项目
android·前端·flutter
Gorit7 小时前
如何使用 Flutter 开发 HarmonyOS 应用
flutter·华为·harmonyos
孤影过客8 小时前
Flutter高性能任务管理APP开发实战代码解析
jvm·flutter·oracle
键盘鼓手苏苏20 小时前
Flutter 三方库 p2plib 的鸿蒙化适配指南 - 实现高性能的端到端(P2P)加密通讯、支持分布式节点发现与去中心化数据流传输实战
flutter·harmonyos·鸿蒙·openharmony
加农炮手Jinx20 小时前
Flutter for OpenHarmony:postgrest 直接访问 PostgreSQL 数据库的 RESTful 客户端(Supabase 核心驱动) 深度解析与鸿蒙适配指南
数据库·flutter·华为·postgresql·restful·harmonyos·鸿蒙