插件介绍

fluttertpc_flutter_blue_plus 是一个功能强大的蓝牙低功耗(BLE)插件,专为 Flutter 应用设计,支持在鸿蒙(HarmonyOS)平台上进行 BLE 设备的扫描、连接和通信。该插件基于 flutter_blue_plus 进行了鸿蒙平台的适配,提供了完整的 BLE 中央角色功能支持。

主要功能特性

  • ✅ 支持检查设备是否支持蓝牙
  • ✅ 支持启用蓝牙适配器
  • ✅ 支持扫描 BLE 设备
  • ✅ 支持与 BLE 设备建立连接
  • ✅ 支持发现设备的服务和特征
  • ✅ 支持读写特征值
  • ✅ 支持设置特征通知
  • ✅ 支持读取设备 RSSI 值
  • ✅ 支持设置 MTU 值
  • ✅ 支持蓝牙适配器状态监听
  • ✅ 支持连接状态变化监听

如何使用插件

1. 包的引入

由于这是一个自定义修改版本的插件,需要通过 Git 形式引入依赖。在项目的 pubspec.yaml 文件中添加以下配置:

dependencies:
  flutter:
    sdk: flutter
  flutter_blue_plus:
    git:
      url: "https://gitcode.com/openharmony-sig/fluttertpc_flutter_blue_plus.git"
      path: "."
  flutter_blue_plus_ohos:
    git:
      url: "https://gitcode.com/openharmony-sig/fluttertpc_flutter_blue_plus.git"
      path: "ohos"

添加依赖后,执行以下命令获取包:

flutter pub get

2. 权限配置

在鸿蒙平台上使用蓝牙功能需要配置相应的权限。打开 entry/src/main/module.json5 文件,添加以下权限配置:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.ACCESS_BLUETOOTH",
        "reason": "需要蓝牙权限来扫描和连接BLE设备",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.PERSISTENT_BLUETOOTH_PEERS_MAC"
      }
    ]
  }
}

同时,在 entry/src/main/resources/base/element/string.json 文件中添加权限说明:

{
  "string": [
    {
      "name": "bluetooth_reason",
      "value": "需要蓝牙权限来扫描和连接BLE设备"
    }
  ]
}

3. API 的调用

3.1 初始化与蓝牙状态检查
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

// 检查设备是否支持蓝牙
bool isSupported = await FlutterBluePlus.isSupported;
if (!isSupported) {
  print("此设备不支持蓝牙");
  return;
}

// 设置日志级别
FlutterBluePlus.setLogLevel(LogLevel.verbose, color: false);

// 监听蓝牙适配器状态变化
StreamSubscription<BluetoothAdapterState> adapterStateSubscription =
    FlutterBluePlus.adapterState.listen((state) {
  print("蓝牙状态: $state");
  if (state == BluetoothAdapterState.on) {
    // 蓝牙已开启,可以开始扫描设备
  } else {
    // 蓝牙未开启,需要提示用户
  }
});

// 开启蓝牙(仅Android和HarmonyOS平台支持)
if (defaultTargetPlatform == TargetPlatform.android ||
    defaultTargetPlatform == TargetPlatform.ohos) {
  await FlutterBluePlus.turnOn(timeout: 60);
}
3.2 扫描 BLE 设备
// 监听扫描结果
StreamSubscription<List<ScanResult>> scanResultsSubscription =
    FlutterBluePlus.scanResults.listen((results) {
  for (ScanResult result in results) {
    print('发现设备: ${result.device.platformName}, RSSI: ${result.rssi}');
    print('设备ID: ${result.device.remoteId}');
    print('广播名称: ${result.advertisementData.advName}');
    print('服务UUID: ${result.advertisementData.serviceUuids}');
  }
});

// 开始扫描
await FlutterBluePlus.startScan(
  withServices: [], // 可以指定要扫描的服务UUID
  withNames: [], // 可以指定要扫描的设备名称
  timeout: Duration(seconds: 30), // 扫描超时时间
);

// 等待扫描停止
await FlutterBluePlus.isScanning.where((val) => val == false).first;

// 清理订阅
scanResultsSubscription.cancel();
3.3 连接 BLE 设备
// 获取扫描结果中的设备
ScanResult scanResult = results.first;
BluetoothDevice device = scanResult.device;

// 监听连接状态变化
StreamSubscription<BluetoothConnectionState> connectionStateSubscription =
    device.connectionState.listen((state) {
  print('连接状态: $state');
  if (state == BluetoothConnectionState.connected) {
    // 连接成功,可以发现服务
    discoverServices(device);
  } else if (state == BluetoothConnectionState.disconnected) {
    // 连接断开
    print('设备已断开连接');
  }
});

// 建立连接
await device.connect(
  timeout: Duration(seconds: 35),
  autoConnect: false,
);
3.4 发现设备服务
Future<void> discoverServices(BluetoothDevice device) async {
  // 发现服务
  List<BluetoothService> services = await device.discoverServices();

  for (BluetoothService service in services) {
    print('服务UUID: ${service.uuid}');

    // 遍历服务的特征
    for (BluetoothCharacteristic characteristic in service.characteristics) {
      print('  特征UUID: ${characteristic.uuid}');
      print('  特征属性: ${characteristic.properties}');

      // 读取特征值(如果有读权限)
      if (characteristic.properties.read) {
        List<int> value = await characteristic.read();
        print('  特征值: $value');
      }

      // 订阅特征通知(如果有通知权限)
      if (characteristic.properties.notify) {
        await characteristic.setNotifyValue(true);
        characteristic.onValueReceived.listen((value) {
          print('  接收到通知值: $value');
        });
      }

      // 写入特征值(如果有写权限)
      if (characteristic.properties.write) {
        List<int> valueToWrite = [1, 2, 3, 4, 5];
        await characteristic.write(valueToWrite, withoutResponse: false);
      }

      // 遍历特征的描述符
      for (BluetoothDescriptor descriptor in characteristic.descriptors) {
        print('    描述符UUID: ${descriptor.uuid}');

        // 读取描述符值
        List<int> descriptorValue = await descriptor.read();
        print('    描述符值: $descriptorValue');
      }
    }
  }
}
3.5 读取 RSSI 值
// 读取设备RSSI值
int rssi = await device.readRssi();
print('设备RSSI: $rssi');
3.6 设置 MTU 值
// 请求设置MTU值
int newMtu = await device.requestMtu(desiredMtu: 512);
print('新的MTU值: $newMtu');

// 监听MTU值变化
device.mtu.listen((mtu) {
  print('MTU值已更新: $mtu');
});
3.7 断开连接
// 断开连接
await device.disconnect(timeout: 35);

// 清理订阅
connectionStateSubscription.cancel();
adapterStateSubscription.cancel();

代码示例

下面是一个完整的示例,展示了如何在鸿蒙应用中使用 fluttertpc_flutter_blue_plus 插件:

import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'dart:typed_data';

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

class BluetoothApp extends StatelessWidget {
  const BluetoothApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BLE 示例应用',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const BluetoothHomePage(),
    );
  }
}

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

  
  State<BluetoothHomePage> createState() => _BluetoothHomePageState();
}

class _BluetoothHomePageState extends State<BluetoothHomePage> {
  BluetoothAdapterState _adapterState = BluetoothAdapterState.unknown;
  List<ScanResult> _scanResults = [];
  bool _isScanning = false;
  BluetoothDevice? _connectedDevice;
  List<BluetoothService> _services = [];

  late StreamSubscription<BluetoothAdapterState> _adapterStateSubscription;
  late StreamSubscription<List<ScanResult>> _scanResultsSubscription;
  StreamSubscription<BluetoothConnectionState>? _connectionStateSubscription;

  
  void initState() {
    super.initState();
    _initBluetooth();
  }

  Future<void> _initBluetooth() async {
    // 检查设备是否支持蓝牙
    bool isSupported = await FlutterBluePlus.isSupported;
    if (!isSupported) {
      _showAlert('此设备不支持蓝牙');
      return;
    }

    // 设置日志级别
    FlutterBluePlus.setLogLevel(LogLevel.verbose, color: false);

    // 监听蓝牙适配器状态
    _adapterStateSubscription = FlutterBluePlus.adapterState.listen((state) {
      setState(() {
        _adapterState = state;
      });
    });

    // 开启蓝牙
    await FlutterBluePlus.turnOn(timeout: 60);
  }

  Future<void> _startScan() async {
    setState(() {
      _scanResults.clear();
      _isScanning = true;
    });

    // 监听扫描结果
    _scanResultsSubscription = FlutterBluePlus.scanResults.listen((results) {
      setState(() {
        _scanResults = results;
      });
    });

    // 开始扫描
    await FlutterBluePlus.startScan(timeout: Duration(seconds: 15));

    // 等待扫描停止
    await FlutterBluePlus.isScanning.where((val) => val == false).first;

    setState(() {
      _isScanning = false;
    });

    // 清理订阅
    _scanResultsSubscription.cancel();
  }

  Future<void> _connectToDevice(BluetoothDevice device) async {
    try {
      setState(() {
        _connectedDevice = device;
      });

      // 监听连接状态
      _connectionStateSubscription = device.connectionState.listen((state) {
        if (state == BluetoothConnectionState.connected) {
          // 连接成功,发现服务
          _discoverServices(device);
        } else if (state == BluetoothConnectionState.disconnected) {
          setState(() {
            _connectedDevice = null;
            _services.clear();
          });
        }
      });

      // 建立连接
      await device.connect(timeout: Duration(seconds: 35));
    } catch (e) {
      _showAlert('连接失败: $e');
      setState(() {
        _connectedDevice = null;
      });
    }
  }

  Future<void> _discoverServices(BluetoothDevice device) async {
    List<BluetoothService> services = await device.discoverServices();
    setState(() {
      _services = services;
    });
  }

  Future<void> _disconnectFromDevice() async {
    if (_connectedDevice != null) {
      await _connectedDevice!.disconnect();
      _connectionStateSubscription?.cancel();
      setState(() {
        _connectedDevice = null;
        _services.clear();
      });
    }
  }

  void _showAlert(String message) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('提示'),
        content: Text(message),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('确定'),
          ),
        ],
      ),
    );
  }

  
  void dispose() {
    _adapterStateSubscription.cancel();
    _connectionStateSubscription?.cancel();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('BLE 示例应用')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 蓝牙状态
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('蓝牙状态', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 8),
                    Text('当前状态: $_adapterState'),
                    const SizedBox(height: 16),
                    ElevatedButton(
                      onPressed: _adapterState == BluetoothAdapterState.on ? _startScan : null,
                      child: Text(_isScanning ? '正在扫描...' : '开始扫描'),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // 扫描结果
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('扫描结果', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 8),
                    _scanResults.isEmpty
                        ? const Text('暂无设备')
                        : SizedBox(
                            height: 200,
                            child: ListView.builder(
                              itemCount: _scanResults.length,
                              itemBuilder: (context, index) {
                                ScanResult result = _scanResults[index];
                                return ListTile(
                                  title: Text(result.device.platformName.isNotEmpty
                                      ? result.device.platformName
                                      : '未知设备'),
                                  subtitle: Text('RSSI: ${result.rssi} dBm'),
                                  trailing: ElevatedButton(
                                    onPressed: () => _connectToDevice(result.device),
                                    child: const Text('连接'),
                                  ),
                                );
                              },
                            ),
                          ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // 连接设备信息
            if (_connectedDevice != null)
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text('已连接设备', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                      const SizedBox(height: 8),
                      Text('设备名称: ${_connectedDevice!.platformName}'),
                      Text('设备ID: ${_connectedDevice!.remoteId}'),
                      const SizedBox(height: 16),
                      ElevatedButton(
                        onPressed: _disconnectFromDevice,
                        child: const Text('断开连接'),
                      ),
                    ],
                  ),
                ),
              ),

            const SizedBox(height: 16),

            // 服务列表
            if (_services.isNotEmpty)
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text('服务列表', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                      const SizedBox(height: 8),
                      for (BluetoothService service in _services)
                        Padding(
                          padding: const EdgeInsets.only(bottom: 16.0),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text('服务UUID: ${service.uuid}'),
                              const SizedBox(height: 8),
                              for (BluetoothCharacteristic characteristic in service.characteristics)
                                Padding(
                                  padding: const EdgeInsets.only(left: 16.0, bottom: 8.0),
                                  child: Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    children: [
                                      Text('特征UUID: ${characteristic.uuid}'),
                                      Text('属性: ${characteristic.properties}'),
                                    ],
                                  ),
                                ),
                            ],
                          ),
                        ),
                    ],
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. 在鸿蒙平台上使用蓝牙功能需要正确配置权限,否则会导致蓝牙功能无法正常工作。

  2. 蓝牙扫描可能会消耗较多电量,建议在不需要时及时停止扫描。

  3. 与 BLE 设备的通信应该在连接建立后进行,通信完成后及时断开连接。

  4. 对于频繁的特征值读写操作,建议使用通知方式获取数据,而不是轮询读取。

  5. MTU 值的设置需要考虑设备的支持情况,过大的 MTU 值可能会导致通信不稳定。

总结

fluttertpc_flutter_blue_plus 插件为鸿蒙平台提供了完整的 BLE 中央角色功能支持,包括设备扫描、连接建立、服务发现、特征读写等核心功能。通过简单的 API 调用,开发者可以轻松地在 Flutter 应用中集成 BLE 通信功能,实现与各种 BLE 设备的交互。

该插件的主要优势在于:

  1. 跨平台兼容性:除了鸿蒙平台外,还支持 Android、iOS、macOS 等多种平台,便于开发者编写跨平台应用。

  2. 完整的功能支持:覆盖了 BLE 中央角色的大多数常见功能需求,满足各种应用场景。

  3. 简单易用的 API:提供了简洁明了的 API 接口,减少了开发者的学习成本。

  4. 稳定的性能:基于成熟的 flutter_blue_plus 进行开发,具有稳定的性能和良好的兼容性。

通过本指南,开发者可以快速上手并在鸿蒙应用中使用 fluttertpc_flutter_blue_plus 插件,实现高效的 BLE 通信功能。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐