Flutter蓝牙通信插件flutter_blue_classic的使用
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
更多关于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();
}
}
注意:
flutter_blue_classic
的API可能与示例代码中的不完全一致,具体请参考其官方文档或源码。- 在实际使用中,可能需要处理更多的异常情况和边缘情况,比如设备连接失败、数据传输中断等。
- 由于蓝牙通信的复杂性,建议在正式项目中充分测试各种场景,确保稳定性和可靠性。
由于flutter_blue_classic
不是官方插件,如果找不到该插件或遇到兼容性问题,可以考虑使用官方推荐的flutter_blue
插件或其他成熟的蓝牙通信库。