Flutter蓝牙连接插件xunil_blue_connect的使用

Flutter蓝牙连接插件xunil_blue_connect的使用

此插件用于基本的蓝牙管理。目前该插件仅支持Android平台,并将在未来增加iOS支持。

当前版本支持经典蓝牙,但即将加入对低功耗蓝牙技术的支持。

功能列表

  • ✅ 检查位置设置
  • ✅ 申请位置权限
  • ✅ 检查蓝牙可用性
  • ✅ 设置蓝牙开启/关闭
  • ✅ 建立/关闭连接
  • ✅ 配对设备
  • ✅ 写入数据
  • ✅ 当断开连接时重新写入数据的状态
  • ✅ 开始/停止搜索
  • ✅ 发现设备
  • ✅ 获取已配对设备的服务UUID
  • ✅ 获取名称和UUID的简短描述
  • ✅ 监听连接和配对状态
  • ✅ 监听开始/停止搜索状态
  • ✅ 支持Android 12
  • ❌ iOS支持
  • ❌ 蓝牙低功耗(BLE)支持
  • ❌ 读取/重置
  • ❌ 多连接
  • ❌ 自动连接
  • ❌ 重新连接
  • ❌ 移除配对

导入库

import 'package:xunil_blue_connect/xunil_blue_connect.dart';

初始化

XunilBlueConnect blueConnect = XunilBlueConnect();

异步使用

检查蓝牙是否可用
await blueConnect.isBluetoothAvailable();
检查位置设置
await blueConnect.checkSettingLocation();
申请位置权限
await blueConnect.applyPermissionLocation();
设置蓝牙开启
await blueConnect.bluetoothSetEnable();
设置蓝牙关闭
await blueConnect.bluetoothSetDisable();

设备扫描

开始扫描设备
await blueConnect.startDiscovery();
监听扫描结果
blueConnect.listenDeviceResults.listen((device){
    print(device);
});

或者使用 StreamBuilder 监听

StreamBuilder(
    stream: blueConnect.listenDeviceResults,
    builder: (context, snapshot) {
	    if (snapshot.hasData) {
		    var device = BluetoothDevice.fromJson(
			    jsonDecode(snapshot.data as String)
		    );
			print(device);
	    }
    },
),
扫描返回参数
  • name -> 设备名称,由硬件提供(默认)
  • aliasName -> 用户指定的设备名称
  • address -> 设备的MAC地址(仅限Android)
  • type -> 设备类型
  • isPaired -> 设备的配对状态
  • uuids -> 设备的UUID(仅限已配对设备)

状态监听

监听发现、连接和配对状态
blueConnect.listenStatus.listen((status){
    print(status);
});

或者使用 StreamBuilder

StreamBuilder(
    stream: blueConnect.listenStatus,
    builder: (context, snapshot) {
	    if (snapshot.hasData) {
		    
		    var  STATUS = jsonDecode(snapshot.data  as  String);

		    // 对于配对状态
			switch (STATUS['STATUS_PAIRING']) {
				case  PairedStatus.PAIRED:
					print(PairedStatus.PAIRED);
					break;
				case  PairedStatus.PAIRING:
					print(PairedStatus.PAIRING);
					break;
				case  PairedStatus.PAIRED_NONE:
					print(PairedStatus.PAIRED_NONE);
					break;
				case  PairedStatus.UNKNOWN_PAIRED:
					print(PairedStatus.UNKNOWN_PAIRED);
					break;
			}

			// 对于连接状态
			switch (STATUS['STATUS_CONNECTING']) {
				case  ConnectingStatus.STATE_CONNECTED:
					print(STATUS['MAC_ADDRESS']);
					print(ConnectingStatus.STATE_CONNECTED);
					break;
				case  ConnectingStatus.STATE_DISCONNECTED:
					print(STATUS['MAC_ADDRESS']);
					print(ConnectingStatus.STATE_DISCONNECTED);
					break;
			}

			// 对于发现状态
			switch (STATUS['STATUS_DISCOVERY']) {
				case  DiscoveryStatus.STARTED:
					print(DiscoveryStatus.STARTED);
					break;
				case  DiscoveryStatus.FINISHED:
					print(DiscoveryStatus.FINISHED);
					break;
			}
	    }
    },
),

建立连接

连接设备
// 默认串口UUID
await blueConnect.connect(macAddress: device.macAddress);

// 或者指定任意UUID
await blueConnect.connect(macAddress: device.macAddress, uuidString: "00001200-0000-1000-8000-00805F9B34FB");
断开连接
// 如果不传参数,则断开最后一个连接
await blueConnect.disconnect();

// 或者指定要断开连接的设备MAC地址
await blueConnect.disconnect(macAddress: device.macAddress);

配对设备

配对设备
await blueConnect.pair(macAddress: device.macAddress);
获取所有已配对设备
await blueConnect.getPairedDevices();

写入数据

写入数据
await blueConnect.write(data: "Lorem ipsum dolor sit amet.", autoConnect: true);

对于蓝牙和位置权限

/android/app/src/main/AndroidManifest.xml 中添加以下权限:

蓝牙权限
<uses-permission  android:name="android.permission.BLUETOOTH"  android:maxSdkVersion="30"  />
<uses-permission  android:name="android.permission.BLUETOOTH_ADMIN"  android:maxSdkVersion="30"  />

// 对于Android 12
<uses-permission  android:name="android.permission.BLUETOOTH_SCAN"  /> 
<uses-permission  android:name="android.permission.BLUETOOTH_ADVERTISE"  />  
<uses-permission  android:name="android.permission.BLUETOOTH_CONNECT"  />
位置权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

示例代码

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:xunil_blue_connect/xunil_blue_connect.dart';
import 'package:xunil_blue_connect_example/device.dart';
import 'package:xunil_blue_connect/utils/status.dart';
import 'package:xunil_blue_connect_example/uuid.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(
            title: const Text('基本蓝牙管理'),
          ),
          body: const Center(
            child: MainBody(),
          ),
        ),
      ),
    );
  }
}

class MainBody extends StatefulWidget {
  const MainBody({Key? key}) : super(key: key);

  [@override](/user/override)
  State<MainBody> createState() => _BodyState();
}

class _BodyState extends State<MainBody> {
  bool _isBluetoothAvailable = false;
  bool _isLocationAvailable = false;
  bool _isLocationOn = false;
  List<BluetoothDevice>? devices = [];
  bool isLoading = false;

  // 调用类
  XunilBlueConnect blueConnect = XunilBlueConnect();

  void bottomsheetForUUIDS(List<UUIDS>? uuids) {
    showModalBottomSheet(
      context: context,
      isDismissible: true,
      backgroundColor: Colors.white,
      builder: (data) {
        return SizedBox(
          height: MediaQuery.of(context).size.height * 0.6,
          child: ListView.builder(
            itemCount: uuids?.length,
            shrinkWrap: true,
            itemBuilder: (context, index) {
              return ListTile(
                visualDensity: VisualDensity.adaptivePlatformDensity,
                style: ListTileStyle.list,
                title: Text(
                  uuids![index].name!.toString(),
                  style: const TextStyle(
                    fontSize: 12.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                subtitle: Text(
                  uuids[index].shortDescription!.toString(),
                  style: const TextStyle(
                    fontSize: 12.0,
                  ),
                ),
                trailing: Text(
                  uuids[index].uuid!.toString(),
                  style: const TextStyle(
                    fontSize: 12.0,
                  ),
                ),
              );
            },
          ),
        );
      },
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SizedBox(
      height: MediaQuery.of(context).size.height * 0.85,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Text('蓝牙是 ${_isBluetoothAvailable ? '开启' : '关闭'}'),
              Text(
                  '位置权限是 ${_isLocationAvailable ? '开启' : '关闭'}'),
              Text('位置设置是 ${_isLocationOn ? '开启' : '关闭'}'),
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(
                      _isBluetoothAvailable ? Colors.lightGreen : Colors.blue),
                ),
                onPressed: () async {
                  // 调用函数并异步执行
                  // 如果函数返回null,表示设备不支持蓝牙
                  var isBlue = await blueConnect.isBluetoothAvailable();
                  setState(() {
                    _isBluetoothAvailable = isBlue;
                  });
                },
                child: const Text('检查蓝牙'),
              ),
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () async {
                  await blueConnect.startDiscovery();
                  setState(() {
                    isLoading = true;
                  });
                  Timer(const Duration(seconds: 13), () async {
                    await blueConnect.stopDiscovery();
                    setState(() {
                      isLoading = false;
                    });
                  });
                },
                child: const Text('开始搜索'),
              ),
              ElevatedButton(
                onPressed: () async {
                  await blueConnect.stopDiscovery();
                  setState(() {
                    isLoading = false;
                  });
                },
                child: const Text('停止搜索'),
              ),
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () async {
                  // 调用函数并异步执行
                  // 打开蓝牙
                  await blueConnect.bluetoothSetEnable();
                },
                child: const Text('打开蓝牙'),
              ),
              ElevatedButton(
                onPressed: () async {
                  // 调用函数并异步执行
                  // 关闭蓝牙
                  await blueConnect.bluetoothSetDisable();
                },
                child: const Text('关闭蓝牙'),
              )
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () async {
                  // 调用函数并异步执行
                  // 如果函数返回null,表示设备的位置权限关闭
                  var apply = await blueConnect.applyPermissionLocation();

                  setState(() {
                    _isLocationAvailable = apply;
                  });
                },
                child: const Text('申请位置权限'),
              ),
              ElevatedButton(
                style: ButtonStyle(
                    backgroundColor: MaterialStateProperty.all(
                        _isLocationOn ? Colors.lightGreen : Colors.blue)),
                onPressed: () async {
                  // 调用函数并异步执行
                  // 如果函数返回null,表示设备的位置关闭
                  var isLocation = await blueConnect.checkSettingLocation();

                  setState(() {
                    _isLocationOn = isLocation;
                  });
                },
                child: const Text('检查位置'),
              )
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () async {
                  // 调用函数并异步执行
                  // 跳转到位置设置页面以启用位置服务
                  await blueConnect.goLocationForEnable();
                },
                child: const Text('跳转到位置设置以启用'),
              )
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () async {
                  var devices = await blueConnect.getPairedDevices();

                  print(devices);
                },
                child: const Text('获取已配对设备'),
              )
            ],
          ),
          if (isLoading)
            const LinearProgressIndicator(color: Colors.orangeAccent),
          StreamBuilder(
            stream: blueConnect.listenStatus,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                var STATUS = jsonDecode(snapshot.data as String);

                // 对于配对状态
                switch (STATUS['STATUS_PAIRING']) {
                  case PairedStatus.PAIRED:
                    print(PairedStatus.PAIRED);
                    break;
                  case PairedStatus.PAIRING:
                    print(PairedStatus.PAIRING);
                    break;
                  case PairedStatus.PAIRED_NONE:
                    print(PairedStatus.PAIRED_NONE);
                    break;
                  case PairedStatus.UNKNOWN_PAIRED:
                    print(PairedStatus.UNKNOWN_PAIRED);
                    break;
                }

                // 对于连接状态
                switch (STATUS['STATUS_CONNECTING']) {
                  case ConnectingStatus.STATE_CONNECTED:
                    print(STATUS['MAC_ADDRESS']);
                    print(ConnectingStatus.STATE_CONNECTED);
                    break;
                  case ConnectingStatus.STATE_DISCONNECTED:
                    print(STATUS['MAC_ADDRESS']);
                    print(ConnectingStatus.STATE_DISCONNECTED);
                    break;
                }

                // 对于发现状态
                switch (STATUS['STATUS_DISCOVERY']) {
                  case DiscoveryStatus.STARTED:
                    print(DiscoveryStatus.STARTED);
                    break;
                  case DiscoveryStatus.FINISHED:
                    print(DiscoveryStatus.FINISHED);
                    break;
                }
              }
              return const SizedBox();
            },
          ),
          StreamBuilder(
            stream: blueConnect.listenDeviceResults,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                var device = BluetoothDevice.fromJson(
                    jsonDecode(snapshot.data as String));

                bool isEmpty = devices!
                    .where(
                      (localAddress) => localAddress.address == device.address,
                    )
                    .isEmpty;

                if (isEmpty) {
                  devices?.add(device);
                }

                return devices!.isNotEmpty
                    ? Expanded(
                        child: ListView.builder(
                          shrinkWrap: true,
                          itemCount: devices?.length,
                          padding: const EdgeInsets.all(10.0),
                          itemBuilder: (context, index) {
                            return ListTile(
                              onLongPress: () {
                                bottomsheetForUUIDS(devices![index].uuids);
                              },
                              onTap: () async {
                                await blueConnect.connect(
                                  macAddress: devices![index].address!,
                                );
                              },
                              title: Text(devices![index].name! +
                                  " (${devices![index].aliasName!})"),
                              subtitle: Text(devices![index].address!),
                              trailing: Row(
                                mainAxisSize: MainAxisSize.min,
                                crossAxisAlignment: CrossAxisAlignment.center,
                                mainAxisAlignment: MainAxisAlignment.end,
                                children: [
                                  SizedBox(
                                    width: 30.0,
                                    child: ElevatedButton(
                                      style: ButtonStyle(
                                        padding: MaterialStateProperty.all(
                                            EdgeInsets.zero),
                                        backgroundColor:
                                            MaterialStateProperty.all(
                                          devices![index].isPaired! == "PAIRED"
                                              ? Colors.lightGreen
                                              : Colors.blue,
                                        ),
                                      ),
                                      onPressed: () async {
                                        await blueConnect.pair(
                                          macAddress: devices![index].address!,
                                        );
                                      },
                                      child: Text(
                                        devices![index].isPaired! == "PAIRED"
                                            ? "C"
                                            : "P",
                                      ),
                                    ),
                                  ),
                                  const SizedBox(
                                    width: 5.0,
                                  ),
                                  if (devices![index].isPaired! == "PAIRED")
                                    SizedBox(
                                      width: 30.0,
                                      child: ElevatedButton(
                                        style: ButtonStyle(
                                          padding: MaterialStateProperty.all(
                                              EdgeInsets.zero),
                                          backgroundColor:
                                              MaterialStateProperty.all(
                                                  Colors.redAccent[400]),
                                        ),
                                        onPressed: () async {
                                          await blueConnect.disconnect();
                                        },
                                        child: const Text("D"),
                                      ),
                                    ),
                                  if (devices![index].isPaired! == "PAIRED")
                                    const SizedBox(
                                      width: 5.0,
                                    ),
                                  if (devices![index].isPaired! == "PAIRED")
                                    SizedBox(
                                      width: 30.0,
                                      child: ElevatedButton(
                                        style: ButtonStyle(
                                          padding: MaterialStateProperty.all(
                                              EdgeInsets.zero),
                                          backgroundColor:
                                              MaterialStateProperty.all(
                                                  Colors.blueGrey),
                                        ),
                                        onPressed: () async {
                                          await blueConnect.write(
                                            data:
                                                "世界是这样的,你知道的",
                                            autoConnect: true,
                                          );
                                        },
                                        child: const Text("W"),
                                      ),
                                    ),
                                ],
                              ),
                            );
                          },
                        ),
                      )
                    : const SizedBox();
              }

              return const SizedBox();
            },
          ),
        ],
      ),
    );
  }
}

更多关于Flutter蓝牙连接插件xunil_blue_connect的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter蓝牙连接插件xunil_blue_connect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


xunil_blue_connect 是一个用于在 Flutter 应用中实现蓝牙连接的插件。虽然它可能不是一个广泛使用的官方插件,但它的功能通常包括扫描设备、连接设备、读写数据等。以下是一个基本的使用指南,帮助你开始使用 xunil_blue_connect 插件。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 xunil_blue_connect 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  xunil_blue_connect: ^版本号

请将 ^版本号 替换为插件的实际版本号。

2. 获取插件实例

在你的 Dart 文件中,首先需要导入插件并获取插件实例。

import 'package:xunil_blue_connect/xunil_blue_connect.dart';

final xunilBlueConnect = XunilBlueConnect();

3. 检查蓝牙状态

在开始扫描设备之前,建议检查蓝牙是否已启用。

Future<void> checkBluetoothStatus() async {
  bool isEnabled = await xunilBlueConnect.isBluetoothEnabled();
  if (!isEnabled) {
    // 请求用户启用蓝牙
    await xunilBlueConnect.requestEnableBluetooth();
  }
}

4. 扫描设备

你可以使用 startScan 方法来扫描附近的蓝牙设备。通常在扫描到设备后,你会得到一个设备列表。

void startScanning() {
  xunilBlueConnect.startScan().listen((device) {
    // 处理扫描到的设备
    print('Found device: ${device.name}, ID: ${device.id}');
  }, onError: (error) {
    // 处理错误
    print('Error occurred while scanning: $error');
  });
}

5. 连接设备

找到设备后,你可以使用 connect 方法来连接设备。

Future<void> connectToDevice(String deviceId) async {
  try {
    await xunilBlueConnect.connect(deviceId);
    print('Connected to device: $deviceId');
  } catch (e) {
    print('Failed to connect to device: $e');
  }
}

6. 读写数据

一旦设备连接成功,你可以使用 writeread 方法来与设备进行数据交互。

Future<void> writeData(String deviceId, List<int> data) async {
  try {
    await xunilBlueConnect.write(deviceId, data);
    print('Data written successfully');
  } catch (e) {
    print('Failed to write data: $e');
  }
}

Future<void> readData(String deviceId) async {
  try {
    List<int> data = await xunilBlueConnect.read(deviceId);
    print('Data read: $data');
  } catch (e) {
    print('Failed to read data: $e');
  }
}

7. 断开连接

在不需要连接时,记得断开连接。

Future<void> disconnectDevice(String deviceId) async {
  try {
    await xunilBlueConnect.disconnect(deviceId);
    print('Disconnected from device: $deviceId');
  } catch (e) {
    print('Failed to disconnect: $e');
  }
}

8. 处理权限

在某些平台上,使用蓝牙可能需要特定的权限。确保你已经在 Android 和 iOS 上处理了所需的权限。

  • Android: 需要在 AndroidManifest.xml 中添加以下权限:

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    
  • iOS: 需要在 Info.plist 中添加以下权限描述:

    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>We need Bluetooth access to connect to devices</string>
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>We need Bluetooth access to connect to devices</string>
    

9. 处理生命周期

在 Flutter 应用中,确保在页面销毁时停止扫描和断开连接。

[@override](/user/override)
void dispose() {
  xunilBlueConnect.stopScan();
  super.dispose();
}
回到顶部