Flutter蓝牙通信插件flutter_blue_classic的使用

发布于 1周前 作者 nodeper 来自 Flutter

Flutter蓝牙通信插件flutter_blue_classic的使用

Flutter_blue_classic 是一个用于与经典蓝牙设备(Bluetooth Classic devices)进行通信的Flutter插件。如果您想使用低功耗蓝牙(BLE)设备,建议考虑 flutter_blue_plus

注意事项 - iOS

该插件目前仅支持Android平台。iOS不提供类似Android的经典蓝牙接口。在iOS上,您的BL设备必须是MFi (Made for iPod/iPhone)认证,并且您需要使用External Accessory Framework。由于这超出了本包的范围,您需要自行实现。

入门指南

minSdkVersion

此包仅兼容Android SDK 21或更高版本,请确保在android/app/build.gradle中设置正确的minSdkVersion

权限

根据Android权限要求的不同情况,此包不在其清单文件中定义任何权限。因此,您需要自己声明它们。详细信息请参阅Android开发者文档中的权限部分。

不需要位置访问权限的情况

android/app/src/main/AndroidManifest.xml中添加以下内容:

<!-- Permissions for Android 12 or above -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- legacy for Android 11 or lower -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />

    <!-- legacy for Android 9 or lower -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />

需要位置访问权限的情况

android/app/src/main/AndroidManifest.xml中添加以下内容:

<!-- Permissions for Android 12 or above -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- legacy for Android 11 or lower -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

    <!-- legacy for Android 9 or lower -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />

然后在初始化插件时传递accessFineLocation参数:

final blueClassic = FlutterBlueClassic(usesFineLocation: true);

参考

FlutterBlueClassic

方法 描述
isSupported 检查设备是否支持蓝牙
isEnabled 检查蓝牙是否已启用
adapterStateNow 当前蓝牙适配器状态
adapterState 蓝牙适配器状态流
bondedDevices 返回已配对设备列表
startScan 开始扫描蓝牙设备
stopScan 停止扫描蓝牙设备
isScanningNow 检查蓝牙适配器是否正在扫描
isScanning 设备是否正在扫描蓝牙设备的状态流
scanResults 扫描期间发现的设备流
turnOn 请求打开蓝牙适配器
bondDevice 请求创建与蓝牙设备的配对
connect 尝试连接到蓝牙设备

BluetoothConnection

方法 描述
input 接收来自连接的蓝牙设备的数据流
output 发送字节数据到连接的蓝牙设备的流接收器
writeString 发送UTF-8编码字符串到远程设备的帮助方法
isConnected 连接是否仍然打开
dispose 在不再需要连接时调用,将调用finish(见下文)
finish 等待所有正在进行的写入完成后优雅地关闭连接
close 立即关闭连接

示例代码

下面是一个完整的示例demo,展示了如何使用flutter_blue_classic插件来扫描、连接和与蓝牙设备通信。

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue_classic/flutter_blue_classic.dart';
import 'package:flutter_blue_classic_example/device_screen.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MainScreen());
  }
}

class MainScreen extends StatefulWidget {
  const MainScreen({super.key});

  @override
  State<MainScreen> createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  final _flutterBlueClassicPlugin = FlutterBlueClassic();

  BluetoothAdapterState _adapterState = BluetoothAdapterState.unknown;
  StreamSubscription? _adapterStateSubscription;

  final Set<BluetoothDevice> _scanResults = {};
  StreamSubscription? _scanSubscription;

  bool _isScanning = false;
  int? _connectingToIndex;
  StreamSubscription? _scanningStateSubscription;

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  Future<void> initPlatformState() async {
    BluetoothAdapterState adapterState = _adapterState;

    try {
      adapterState = await _flutterBlueClassicPlugin.adapterStateNow;
      _adapterStateSubscription =
          _flutterBlueClassicPlugin.adapterState.listen((current) {
        if (mounted) setState(() => _adapterState = current);
      });
      _scanSubscription =
          _flutterBlueClassicPlugin.scanResults.listen((device) {
        if (mounted) setState(() => _scanResults.add(device));
      });
      _scanningStateSubscription =
          _flutterBlueClassicPlugin.isScanning.listen((isScanning) {
        if (mounted) setState(() => _isScanning = isScanning);
      });
    } catch (e) {
      if (kDebugMode) print(e);
    }

    if (!mounted) return;

    setState(() {
      _adapterState = adapterState;
    });
  }

  @override
  void dispose() {
    _adapterStateSubscription?.cancel();
    _scanSubscription?.cancel();
    _scanningStateSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    List<BluetoothDevice> scanResults = _scanResults.toList();

    return Scaffold(
      appBar: AppBar(
        title: const Text('FlutterBlueClassic example app'),
      ),
      body: ListView(
        children: [
          ListTile(
            title: const Text("Bluetooth Adapter state"),
            subtitle: const Text("Tap to enable"),
            trailing: Text(_adapterState.name),
            leading: const Icon(Icons.settings_bluetooth),
            onTap: () => _flutterBlueClassicPlugin.turnOn(),
          ),
          const Divider(),
          if (scanResults.isEmpty)
            const Center(child: Text("No devices found yet"))
          else
            for (var (index, result) in scanResults.indexed)
              ListTile(
                title: Text("${result.name ?? "???"} (${result.address})"),
                subtitle: Text(
                    "Bondstate: ${result.bondState.name}, Device type: ${result.type.name}"),
                trailing: index == _connectingToIndex
                    ? const CircularProgressIndicator()
                    : Text("${result.rssi} dBm"),
                onTap: () async {
                  BluetoothConnection? connection;
                  setState(() => _connectingToIndex = index);
                  try {
                    connection =
                        await _flutterBlueClassicPlugin.connect(result.address);
                    if (!this.context.mounted) return;
                    if (connection != null && connection.isConnected) {
                      if (mounted) setState(() => _connectingToIndex = null);
                      Navigator.push(
                          context,
                          MaterialPageRoute(
                              builder: (context) =>
                                  DeviceScreen(connection: connection!)));
                    }
                  } catch (e) {
                    if (mounted) setState(() => _connectingToIndex = null);
                    if (kDebugMode) print(e);
                    connection?.dispose();
                    ScaffoldMessenger.maybeOf(context)?.showSnackBar(
                        const SnackBar(
                            content: Text("Error connecting to device")));
                  }
                },
              )
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          if (_isScanning) {
            _flutterBlueClassicPlugin.stopScan();
          } else {
            _scanResults.clear();
            _flutterBlueClassicPlugin.startScan();
          }
        },
        label: Text(_isScanning ? "Scanning..." : "Start device scan"),
        icon: Icon(_isScanning ? Icons.bluetooth_searching : Icons.bluetooth),
      ),
    );
  }
}

以上代码展示了一个简单的Flutter应用,它能够扫描附近的蓝牙设备并尝试连接。您可以在此基础上扩展功能,例如发送和接收数据等操作。希望这个示例能帮助您更好地理解和使用flutter_blue_classic插件。


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

1 回复

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


当然,以下是如何在Flutter项目中使用flutter_blue_classic插件进行蓝牙通信的示例代码。请注意,flutter_blue_classic插件并非官方的Flutter蓝牙插件,而是一个社区提供的解决方案,因此其API和可用性可能会有所不同。以下示例基于假设该插件具有类似flutter_blue(官方蓝牙插件)的功能。

首先,确保在pubspec.yaml文件中添加flutter_blue_classic依赖项:

dependencies:
  flutter:
    sdk: flutter
  flutter_blue_classic: ^x.y.z  # 请替换为实际版本号

然后,运行flutter pub get来安装依赖项。

接下来,是一个简单的Flutter应用示例,展示如何使用flutter_blue_classic进行蓝牙设备扫描和连接:

import 'package:flutter/material.dart';
import 'package:flutter_blue_classic/flutter_blue_classic.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Bluetooth Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BluetoothScreen(),
    );
  }
}

class BluetoothScreen extends StatefulWidget {
  @override
  _BluetoothScreenState createState() => _BluetoothScreenState();
}

class _BluetoothScreenState extends State<BluetoothScreen> {
  FlutterBlueClassic flutterBlueClassic = FlutterBlueClassic.instance;
  List<BluetoothDevice> devices = [];
  BluetoothDevice? selectedDevice;
  BluetoothConnection? connection;

  @override
  void initState() {
    super.initState();
    initBluetooth();
  }

  Future<void> initBluetooth() async {
    try {
      await flutterBlueClassic.start();
      await flutterBlueClassic.scanForDevices(timeout: Duration(seconds: 5));
      flutterBlueClassic.devices.listen((List<BluetoothDevice> result) {
        setState(() {
          devices = result;
        });
      });
    } catch (e) {
      print('Error starting Bluetooth: $e');
    }
  }

  Future<void> connectToDevice(BluetoothDevice device) async {
    try {
      selectedDevice = device;
      connection = await device.connect();
      print('Connected to ${device.name}');
      // 这里可以添加连接后的处理逻辑,比如监听数据
    } catch (e) {
      print('Error connecting to device: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Bluetooth Devices'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Expanded(
            child: ListView.builder(
              itemCount: devices.length,
              itemBuilder: (context, index) {
                final device = devices[index];
                return ListTile(
                  title: Text(device.name ?? 'Unknown Device'),
                  onTap: () => connectToDevice(device),
                );
              },
            ),
          ),
          if (selectedDevice != null)
            Text('Connected to: ${selectedDevice?.name}'),
        ],
      ),
    );
  }

  @override
  void dispose() {
    connection?.close();
    flutterBlueClassic.stopScan();
    super.dispose();
  }
}

注意

  1. flutter_blue_classic的API可能与示例代码中的不完全一致,具体请参考其官方文档或源码。
  2. 在实际使用中,可能需要处理更多的异常情况和边缘情况,比如设备连接失败、数据传输中断等。
  3. 由于蓝牙通信的复杂性,建议在正式项目中充分测试各种场景,确保稳定性和可靠性。

由于flutter_blue_classic不是官方插件,如果找不到该插件或遇到兼容性问题,可以考虑使用官方推荐的flutter_blue插件或其他成熟的蓝牙通信库。

回到顶部