Flutter BLE应用的开发-flutter_blue

Posted Andy__Wu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter BLE应用的开发-flutter_blue相关的知识,希望对你有一定的参考价值。

一、写在前面的话

本文主要介绍在Flutter环境下开发BLE应用。主要包含以下内容:判断蓝牙是否开启、判断是否有位置权限、扫描设备、

连接设备、监听设备上报的数据(Notify)、向设备发送数据、监听设备的断开等。内容虽然简单,但是很详细。

二、开始

1、使用到的库

  flutter_blue: ^0.7.1+1
  permission_handler: "^3.2.0" # 权限
  众所周知,Flutter要使用原生的能力,就需要有原生库的支持,这里我们使用了flutter_blue来开发跟BLE相关的功能,permission_handler来开发权限检测以及申请权限的功能。在使用flutter_blue开启扫描的时候,插件会弹出系统的权限申请窗口,但是点击授权以后报了一个空指针,可能是插件存在的一个BUG,所以这里我们用permission_handler插件来处理权限相关的问题。(友情提示:在android上扫描Ble需要位置权限)

2、flutter_blue的用法

import 'package:flutter_blue/flutter_blue.dart';
FlutterBlue flutterBlue = FlutterBlue.instance;
导包和初始化。

3、判断蓝牙是否开启

@override
  void initState()
    super.initState();
    flutterBlue.state.listen((state)
      if(state == BluetoothState.on)
        print('蓝牙状态为开启');
        isBleOn = true;
      else if(state == BluetoothState.off)
        print('蓝牙状态为关闭');
        isBleOn = false;
     
    );
 
在路由初始化的时候开启对蓝牙状态的监听。

 if(!isBleOn)
      ToastUtils.toast(context, "手机蓝牙未打开,请打开后再扫描设备");
      return;
   
在开始之前对蓝牙开启状态进行判断。

4、判断位置权限

PermissionUtils.checkPermissions(PermissionGroup.location).then((v)
      if (v)
        Navigator.pushNamed(context, "/device_page");
      else
        PermissionUtils.showDialog(context, "提示", "扫描蓝牙需要位置权限", () async
          Navigator.pop(context);
          await PermissionHandler()
              .requestPermissions([PermissionGroup.location]);
          PermissionStatus permission = await PermissionHandler()
              .checkPermissionStatus(PermissionGroup.location);
          if (permission == PermissionStatus.granted)
            Navigator.pushNamed(context, "/device_page");
          else
            print("no Permission to scan ble");
            ToastUtils.toast(context, "权限开启失败,请在系统设置中开启!");
         
        , ()
          Navigator.pop(context);
        );
     
    );
 以上是对位置权限的处理,如果有,就进入扫描设备页面,没有的话申请权限,申请完之后在对其进行判断。因为本文的重点是ble,这里不对权限申请插件做过多的介绍。(PermissionUtils,dialog的源码会在文末附上)

5、扫描设备

 flutterBlue.scan().listen((scanResult)
      // do something with scan result
      var device = scanResult.device;
      if (device.name.length > 10)
        if (deviceSet.indexOf(device) == -1)
          setState(()
            deviceSet.add(device);
          );
       
        print(
            '$device.name found! rssi: $scanResult.rssi,address:$device.id');
     
    );
    使用api进行ble扫描,我对蓝牙名进行了过滤,记得要存起来哦,连接的时候要用的。

6、连接设备

      await device.connect(autoConnect: false, timeout: Duration(seconds: 10));
这里的连接参数可以根据需要自己进行设置,我这里设置了10秒连接超时。这里我们一般还不能认为连接成功,还需要找到对应的读写服务和特征值。

BluetoothCharacteristic mCharacteristic;
List<BluetoothService> services = await device.discoverServices();
      services.forEach((service)
        if (service.uuid.toString() == GattAttributes.BLE_SPP_SERVICE_READ)
          List<BluetoothCharacteristic> characteristics =
              service.characteristics;
          characteristics.forEach((characteristic)
            if (characteristic.uuid.toString() ==
                GattAttributes.BLE_SPP_NOTIFY_CHARACTERISTIC)
              mCharacteristic = characteristic;
           
          );
       
        // do something with service
      );
以上便是根据uuid在对应的读写服务中找到对应Characteristic,至此,连接过程便已经完成了,可以进行页面跳转了。

7、读取设备的心跳

if (mNotifyCharacteristic != null)
      mNotifyCharacteristic.setNotifyValue(true);
      mNotifyCharacteristic.value
          .listen((value) => print("device is online: " + value.toString()));
   
通过第6点的方法,找到可以notify的Characteristic,开启notify,然后监听其值。这里的setNotifyValue可能会有异常,具体请

参考我的前篇文章点我点我。下图是蓝牙设备传回的心跳。

8、向设备发送数据

  Future<Null> write(List<int> value, bool withoutResponse = false)
我先查看源码可知write方法的参数是一个int型的list,那么我们只需把我们需要发送的数据放进这个list中就行了。

      mWriteCharacteristic.write([0x00,0x01]);
同样,获取mWriteCharacteristic的方法如上第6点所示。

9、监听设备的断开

  device.state.listen((state)
        if(state == BluetoothDeviceState.disconnected)
          DialogUtils.showOneDialog(context, "提示", "设备已断开连接", ()
//do something
          );
       
      );
10、在不用页面获取device

 await flutterBlue.connectedDevices.then((list) =>
          if (list.length == 0) Navigator.pop(context) else device = list[0]
        );
在不同的页面(路由)需要对设备进行读写操作,在设备的连接池中获取。

三、总结

本文归纳了flutter下使用ble对设备进行读写的详细步骤以及一些基本方法和注意事项,如果有问题的同学欢迎留言,博主会一一解答的。技术在于分享,开源的乐趣也在于此,如果本文有不够严谨的地方还望大佬支持。

附录

import 'package:flutter/cupertino.dart';
 
class DialogUtils
  static showDialog(BuildContext cxt, String title, String content,
      ok(), cancel())
    showCupertinoDialog<int>(
        context: cxt,
        builder: (cxt)
          return CupertinoAlertDialog(
            title: Text(title),
            content: Text(content),
            actions: <Widget>[
              CupertinoDialogAction(
                child: Text("确定"),
                onPressed: ()
                  ok();
                ,
              ),
              CupertinoDialogAction(
                child: Text("取消"),
                onPressed: ()
                  cancel();
                ,
              )
            ],
          );
        );
 
  static showOneDialog(BuildContext cxt, String title, String content,
      ok())
    showCupertinoDialog<int>(
        context: cxt,
        builder: (cxt)
          return CupertinoAlertDialog(
            title: Text(title),
            content: Text(content),
            actions: <Widget>[
              CupertinoDialogAction(
                child: Text("确定"),
                onPressed: ()
                  ok();
                ,
              )
            ],
          );
        );
 

import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
 
/// 权限管理工具类
class PermissionUtils
  /// 检测相关权限是否已经打开(根据已有状态值)
  static bool checkPermissionsByStatus(List<PermissionStatus> lists)
    bool result = true;
 
    for (PermissionStatus permissionStatus in lists)
      if (permissionStatus != PermissionStatus.granted)
        result = false;
        break;
     
   
 
    return result;
 
 
  /// 检测相关权限是否已经打开(根据已有权限名称)
  static Future<bool> checkPermissionsByGroup(
      List<PermissionGroup> lists) async
    bool result = true;
 
    for (PermissionGroup permissionGroup in lists)
      PermissionStatus checkPermissionStatus =
      await PermissionHandler().checkPermissionStatus(permissionGroup);
 
      if (checkPermissionStatus != PermissionStatus.granted)
        result = false;
        break;
     
   
 
    return result;
 
  static Future<bool> checkPermissions(PermissionGroup permissionGroup) async
    bool result = true;
 
    PermissionStatus checkPermissionStatus =
    await PermissionHandler().checkPermissionStatus(permissionGroup);
 
    if (checkPermissionStatus != PermissionStatus.granted)
      result = false;
 
   
    return result;
 
 
 
  /// 权限提示对话款
  static showDialog(BuildContext cxt, String title, String content,
       ok(), cancel())
    showCupertinoDialog<int>(
        context: cxt,
        builder: (cxt)
          return CupertinoAlertDialog(
            title: Text(title),
            content: Text(content),
            actions: <Widget>[
              CupertinoDialogAction(
                child: Text("去开启"),
                onPressed: ()
                  ok();
                ,
              ),
              CupertinoDialogAction(
                child: Text("取消"),
                onPressed: ()
                  cancel();
                ,
              )
            ],
          );
        );
 

以上是关于Flutter BLE应用的开发-flutter_blue的主要内容,如果未能解决你的问题,请参考以下文章

Flutter BLE应用的开发-flutter_blue

Flutter BLE应用的开发-flutter_blue

flutter ble插件

flutter 蓝牙ble(blue tooth),同时连接多台/多个设备

Flutter:如何显示同一张表中的所有列

根据 Flutter 中的 Provider 值变化在屏幕之间导航