flutter 蓝牙相关(从零开发一个蓝牙万用表App)

前言

在生活中,使用到蓝牙的场景很多,如共享单车,智能手环,蓝牙打印机,蓝牙万用表,蓝牙门锁等
今天我们讨论在flutter中如何开发与蓝牙相关的app。
小伙伴们对蓝牙使用流程不是很了解的情况,建议去恶补一下。我这里也只是简单提到

0.打开蓝牙,蓝牙权限判断等
1.扫描设备,找到目标设备
2.连接设备
3.扫描已连接设备下的服务,可能有多个服务(fff0,fff1,fff2,fff3,fff4,fff5等)
4.针对某一个服务,扫描它的特征,可能有多个特征
5.有些特征是可读,可写,有的特征只能读操作,有的只能写操作,有的特征只通知
6.根据不同的特征,做相应的处理,可以对下位机发命令,也可以读取下位机的相关信息等

image.png
正题

下面将从零开始,在flutter里面写一个关于蓝牙万用表的app

一.创建工程

1.创建一个项目,如下图


1.png

2.然后就是下一步,下一步,直到最后一步,要勾选支持swift,kotlin的选项,因为现在很多第三方库都要支持swift,要不然在后面引入第三方库时,会有很多报错!


last.png

3.创建完工程后,点击到pubspec.yaml文件,点击右上角的Pub get,获取一下依赖,然后点击运行,看能不能正常跑起来


3.png

4.如果没有报错,跳过此步骤,如果报错了,去原生工程里面配置一下
4.1 点击工程文件,右键一下,如下图


image.png

4.2 配置一下iOS 开发者团队,如果没有话,就去申请开发都账号吧


4.2.png

5.再回到flutter工程,把工程跑起来

二.选择第三方库,封装一个蓝牙工具,方便使用及后期的维护工作

我这里选择了https://pub.dev/packages/flutter_blue,flutter_blue这个蓝牙库,他适配了iOS Android,也支持了空安全。
有了第三方库,我们需要对flutter_blue进行简单封装一下,ble_tool,具体代码如下:

import 'dart:async';
import 'package:flutter_blue/flutter_blue.dart';
import 'dart:developer';
import 'dart:core';

typedef ScanResultHandler = void Function(List<ScanResult>? datas);
typedef CharacteristicHandler = void Function(List<int>? datas);

class BleManager {
  FlutterBlue? _flutterBlue;
  factory BleManager() => _getInstance();
  static BleManager get instance => _getInstance();
  static  BleManager? _instance;
   BleManager._internal() {
    _flutterBlue = FlutterBlue.instance;
  }
  static BleManager _getInstance() {
    if (_instance == null) {
      _instance = BleManager._internal();
    }
    return _instance!;
  }

  startScan(ScanResultHandler dataHandler, {int timeout = 60}) {
    log("_scan");
    // _flutterBlue?.startScan(timeout: Duration(seconds: timeout));

    _flutterBlue?.startScan(timeout: Duration(seconds: timeout),withServices: [Guid("0000fff0-0000-1000-XXXX-XXXXXXXXXXX")]);
    _flutterBlue?.scanResults.listen(dataHandler);
  }

  stopScan() {
    log("_stopScan");
    _flutterBlue?.stopScan();
  }

  Future<void> connectDevice(BluetoothDevice device) async {
    log("_connect");
    await device.connect(autoConnect: false);
  }

  Future<List<BluetoothService>> deviceToDiscoverServices(
      BluetoothDevice device) async {
    return await device.discoverServices();
  }

  Future<bool> characteristicToSetNotifyValue(
      BluetoothCharacteristic characteristic) async {
    return await characteristic.setNotifyValue(true);
  }

  listenCharacteristicValue(BluetoothCharacteristic characteristic,
      CharacteristicHandler characteristicHandler) {
    characteristic.value.listen(characteristicHandler);
  }

  Future<Null> characteristicToWriteValue(
      BluetoothCharacteristic characteristic, List<int> list,{bool withoutResponse = false}) async {
    log("_write  withoutResponse = $withoutResponse",);
    return await characteristic.write(list,withoutResponse: withoutResponse);
  }

  Future<List<int>> characteristicToReadValue(
      BluetoothCharacteristic characteristic) async {
    log("_read");
    return await characteristic.read();
  }

  Future<List<BluetoothDevice>>? getConnectedDevices() {
    return _flutterBlue?.connectedDevices;
  }

  Future<dynamic> disconnectDevice(BluetoothDevice device) async {
    log("_disconnect");
    return await device.disconnect();
  }
}

三.使用封装好的蓝牙工具类ble_tool

使用之前需要对iOS工程,Android工程加一些权限
iOS 如下图


iOS.png

android如下图


gradle.png
android.png

开始扫描

    BleManager.instance.startScan(scanResultHandler, timeout: 10);

扫描结果监听

  void scanResultHandler(List<ScanResult>? results) {
    print('results ====> $results');

    for (ScanResult r in results!) {
      print('${r.device.name} found! rssi: ${r.rssi}');

    }
  }

停止扫描

    BleManager.instance.stopScan();

连接设备并且去扫描设备的服务services

    BleManager.instance.connectDevice(widget.device).then((e) {
      _discoverServices();
    });

扫描设备服务,过滤服务0000fff0-0000-1000-XXXX-XXXXXXXXXXX,简称fff0, 并且扫描fff0服务下的所有特征
(_characteristicForFFF1,_characteristicForFFF2等)

  _discoverServices() async {
    List<BluetoothService> services =
        await BleManager.instance.deviceToDiscoverServices(widget.device);

    print("+++++++$services");
    if (services == null) return;
    services.forEach((service) {
      // do something with service
      print("uuid=${service.uuid}  deviceId=${service.deviceId}");
      if (service.uuid.toString() == "0000fff0-0000-1000-XXXX-XXXXXXXXXXX") {
        print("---service is fff0---");

        var characteristics = service.characteristics;
        for (BluetoothCharacteristic c in characteristics) {
          // log("----BluetoothCharacteristic-->$c");
          print("----BluetoothCharacteristicUUID-->${c.uuid}");
          if (c.uuid.toString().contains("fff1")) {
            print("is fff1");
            _characteristicForFFF1 = c;
          } else if (c.uuid.toString().contains("fff2")) {
            print("is fff2");
            _characteristicForFFF2 = c;
            _readDeviceBasicInfo();
          } else if (c.uuid.toString().contains("fff3")) {
            print("is fff3");
            _characteristicForFFF3 = c;
          } else if (c.uuid.toString().contains("fff4")) {
            print("is fff4");
            _characteristicForFFF4 = c;
            _characteristicFFF4SetNotify();//fff4这个特征是只通知的,所以要设置通知setNotify,后面可以监听特征值上报
          } else if (c.uuid.toString().contains("fff5")) {
            print("is fff5");
          }
        }
      }
    });
  }

断开蓝牙连接

    BleManager.instance
        .disconnectDevice(widget.device)
        .then((value) => print("disconnected !!"));

监听特征值上报,主要的数据来自这里

  _characteristicFFF4SetNotify() async {
    await _characteristicForFFF4!.setNotifyValue(true);
    // ignore: missing_return
    BleManager.instance.listenCharacteristicValue(_characteristicForFFF4!,
        (data) {
      print("object=>>>>>>$data");
      if (data == null) {
        print("data=null>>>>>>");
        return;
      }
    }
  }

对设备设置命令,发送请求(写操作)

  BleManager.instance
        .characteristicToWriteValue(_characteristicForFFF3!, list)
        .then((value) => print("write result:$value"));

读取设备的信息(读操作)

 BleManager.instance
        .characteristicToReadValue(_characteristicForFFF2!)
        .then((value) {
      print("fff2 value=$value");
    });

这样蓝牙的相关操作,基本全部结束了,最后来看一下效果吧

999.gif

结尾
蓝牙其他相关文章

flutter 实战App 之 提词器app
flutter 蓝牙ble(blue tooth),同时连接多台/多个设备
iOS 解决蓝牙配对弹框的问题,解决ANCS带来的问题,获取已配对(已绑定)设备列表

flutter的蓝牙还是挺方便的, 做一般的蓝牙应用还是可以的哦,小伴们,觉得有点用的话,或者已经看到这里面来的请点个赞吧~~ 后续分享更多有关flutter的文章。如果有疑问的话,请在下方留言~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容