Flutter蓝牙信标扫描插件beacon_scanner的使用

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

Flutter蓝牙信标扫描插件beacon_scanner的使用

简介

beacon_scanner 是一个基于 flutter_beacon 的插件,由Eyro Labs开发。该插件可以与Android的前台服务结合使用,并在底层使用了Android的AltBeacon和iOS的CoreLocation。它是一个混合iBeacon扫描SDK,支持Android API 18+和iOS 13+。

功能特性

  • 自动权限管理
  • 扫描(Ranging)iBeacons
  • 监控(Monitoring)iBeacons

安装

1. 添加依赖

pubspec.yaml 文件中添加以下依赖:

dependencies:
  flutter_beacon: latest
2. Android平台特定设置

对于目标SDK版本29+(Android 10, 11),需要手动添加 ACCESS_FINE_LOCATION 权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

如果你还需要后台扫描功能,还需添加 ACCESS_BACKGROUND_LOCATION 权限:

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
3. iOS平台特定设置

仅支持iOS 13+。为了使用信标相关功能,应用程序需要请求位置权限。这是一个两步过程:

  1. 在配置文件中声明应用程序所需的权限。
  2. 在应用程序运行时请求用户权限(插件可以自动处理)。

iOS所需权限为“使用时”权限。权限必须在 ios/Runner/Info.plist 中声明:

<dict>
  <!-- 使用时 -->
  <key>NSLocationWhenInUseUsageDescription</key>
  <string>应用程序需要位置的原因</string>
  <!-- 始终 -->
  <!-- 对于iOS 11+ -->
  <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
  <string>应用程序需要位置的原因</string>
  <!-- 对于iOS 9/10 -->
  <key>NSLocationAlwaysUsageDescription</key>
  <string>应用程序需要位置的原因</string>
  <!-- 蓝牙隐私 -->
  <!-- 对于iOS 13+ -->
  <key>NSBluetoothAlwaysUsageDescription</key>
  <string>应用程序需要蓝牙的原因</string>
</dict>

iOS故障排除

  • 示例代码仅在物理设备上正常工作(模拟器上的蓝牙功能已禁用)。
  • 如果示例代码在设备上无法正常工作(信标未出现),请确保已启用位置蓝牙(设置 -> Flutter Beacon)。

使用方法

初始化库
final BeaconScanner beaconScanner = BeaconScanner.instance;

try {
  // false - 如果你想手动检查所需的权限
  await beaconScanner.initialize(true);
} on PlatformException catch(e) {
  // 库初始化失败,检查错误代码和消息
}
扫描(Ranging)信标
final regions = <Region>[];

if (Platform.isIOS) {
  // iOS平台,至少设置identifier和proximityUUID用于区域扫描
  regions.add(Region(
      identifier: 'Apple Airlocate',
      beaconId: IBeaconId(proximityUUID: 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'),
  ));
} else {
  // Android平台,它可以扫描所有Proximity UUID的信标
  regions.add(Region(identifier: 'com.beacon'));
}

// 开始扫描信标
_streamRanging = beaconScanner.ranging(regions).listen((ScanResult result) {
  // result包含一个区域和找到的信标列表
  // 如果范围内没有匹配的信标,列表可能是空的
});

// 停止扫描信标
_streamRanging.cancel();
监控(Monitoring)信标
final regions = <Region>[];

if (Platform.isIOS) {
  // iOS平台,至少设置identifier和proximityUUID用于区域监控
  regions.add(Region(
    identifier: 'Apple Airlocate',
    beaconId: IBeaconId(proximityUUID: 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'),
  ));
} else {
  // Android平台,它可以监控所有Proximity UUID的信标
  regions.add(Region(identifier: 'com.beacon'));
}

// 开始监控信标
// 即使应用程序关闭,iOS上也可以继续工作
_streamMonitoring = beaconScanner.monitoring(regions).listen((MonitoringResult result) {
  // result包含一个区域、事件类型和事件状态
});

// 停止监控信标
_streamMonitoring.cancel();

完整示例Demo

以下是一个完整的Flutter项目示例,展示了如何使用 beacon_scanner 插件来扫描和监控信标。

pubspec.yaml
name: beacon_scanner_example
description: A new Flutter project.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_beacon: latest

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_beacon/flutter_beacon.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Beacon Scanner Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BeaconScannerPage(),
    );
  }
}

class BeaconScannerPage extends StatefulWidget {
  [@override](/user/override)
  _BeaconScannerPageState createState() => _BeaconScannerPageState();
}

class _BeaconScannerPageState extends State<BeaconScannerPage> {
  final BeaconScanner beaconScanner = BeaconScanner.instance;
  StreamSubscription<ScanResult>? _streamRanging;
  StreamSubscription<MonitoringResult>? _streamMonitoring;
  List<Beacon> _beacons = [];
  String _monitoringStatus = 'Not monitoring';

  [@override](/user/override)
  void initState() {
    super.initState();
    _initializeBeaconScanner();
  }

  Future<void> _initializeBeaconScanner() async {
    try {
      await beaconScanner.initialize(true);
      setState(() {
        _monitoringStatus = 'Initialized';
      });
    } on PlatformException catch (e) {
      setState(() {
        _monitoringStatus = 'Failed to initialize: ${e.message}';
      });
    }
  }

  void _startRanging() {
    final regions = <Region>[];

    if (Platform.isIOS) {
      regions.add(Region(
        identifier: 'Apple Airlocate',
        beaconId: IBeaconId(proximityUUID: 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'),
      ));
    } else {
      regions.add(Region(identifier: 'com.beacon'));
    }

    _streamRanging?.cancel();
    _streamRanging = beaconScanner.ranging(regions).listen((result) {
      setState(() {
        _beacons = result.beacons;
      });
    });
  }

  void _stopRanging() {
    _streamRanging?.cancel();
    setState(() {
      _beacons = [];
    });
  }

  void _startMonitoring() {
    final regions = <Region>[];

    if (Platform.isIOS) {
      regions.add(Region(
        identifier: 'Apple Airlocate',
        beaconId: IBeaconId(proximityUUID: 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'),
      ));
    } else {
      regions.add(Region(identifier: 'com.beacon'));
    }

    _streamMonitoring?.cancel();
    _streamMonitoring = beaconScanner.monitoring(regions).listen((result) {
      setState(() {
        _monitoringStatus = 'Monitoring: ${result.state.name}';
      });
    });
  }

  void _stopMonitoring() {
    _streamMonitoring?.cancel();
    setState(() {
      _monitoringStatus = 'Not monitoring';
    });
  }

  [@override](/user/override)
  void dispose() {
    _streamRanging?.cancel();
    _streamMonitoring?.cancel();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Beacon Scanner Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Monitoring Status: $_monitoringStatus',
              style: TextStyle(fontSize: 18),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                if (_streamMonitoring == null) {
                  _startMonitoring();
                } else {
                  _stopMonitoring();
                }
              },
              child: Text(_streamMonitoring == null ? 'Start Monitoring' : 'Stop Monitoring'),
            ),
            SizedBox(height: 20),
            Text(
              'Ranging Beacons:',
              style: TextStyle(fontSize: 18),
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                if (_streamRanging == null) {
                  _startRanging();
                } else {
                  _stopRanging();
                }
              },
              child: Text(_streamRanging == null ? 'Start Ranging' : 'Stop Ranging'),
            ),
            SizedBox(height: 20),
            Expanded(
              child: ListView.builder(
                itemCount: _beacons.length,
                itemBuilder: (context, index) {
                  final beacon = _beacons[index];
                  return ListTile(
                    title: Text('Beacon ${index + 1}'),
                    subtitle: Text(
                      'UUID: ${beacon.proximityUUID}, Major: ${beacon.major}, Minor: ${beacon.minor}, RSSI: ${beacon.rssi}',
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

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

1 回复

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


当然,以下是如何在Flutter项目中使用beacon_scanner插件进行蓝牙信标扫描的示例代码。这个插件允许你扫描附近的蓝牙信标并获取它们的信息。

首先,确保你已经在pubspec.yaml文件中添加了beacon_scanner依赖:

dependencies:
  flutter:
    sdk: flutter
  beacon_scanner: ^x.y.z  # 请替换为最新版本号

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

接下来,在你的Flutter项目中,你可以按照以下步骤进行蓝牙信标扫描:

  1. 初始化插件并请求权限
import 'package:flutter/material.dart';
import 'package:beacon_scanner/beacon_scanner.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  BeaconScanner _beaconScanner = BeaconScanner();
  List<ScanResult> _scanResults = [];

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

  Future<void> _requestPermissions() async {
    // 请求位置权限,因为蓝牙扫描通常需要位置权限
    var status = await Permission.locationWhenInUse.status;
    if (!status.isGranted) {
      var result = await Permission.locationWhenInUse.request();
      if (result.isGranted) {
        // 开始扫描
        _startScanning();
      } else if (result.isPermanentlyDenied) {
        // 处理权限被拒绝的情况
        print("Location permission is permanently denied");
      }
    } else {
      // 权限已授予,开始扫描
      _startScanning();
    }
  }

  void _startScanning() {
    _beaconScanner.startScanning().listen((scanResult) {
      setState(() {
        _scanResults.add(scanResult);
      });
    }).onError((error) {
      print("Scanning failed: $error");
    }).onDone(() {
      print("Scanning stopped");
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Beacon Scanner'),
        ),
        body: ListView.builder(
          itemCount: _scanResults.length,
          itemBuilder: (context, index) {
            var result = _scanResults[index];
            return ListTile(
              title: Text('UUID: ${result.beacon.uuid}'),
              subtitle: Text('Distance: ${result.beacon.distance?.toString()}'),
            );
          },
        ),
      ),
    );
  }

  @override
  void dispose() {
    _beaconScanner.stopScanning();
    super.dispose();
  }
}
  1. 处理权限请求: 上面的代码使用了permission_handler插件来处理权限请求。你需要在pubspec.yaml文件中添加这个依赖:
dependencies:
  permission_handler: ^x.y.z  # 请替换为最新版本号
  1. 权限处理逻辑: 在代码中,我们首先检查位置权限的状态,如果权限未被授予,则请求权限。如果权限被授予,则开始扫描蓝牙信标。

  2. 扫描结果展示: 扫描到的结果会被存储在一个列表中,并通过ListView.builder展示在UI上。

  3. 停止扫描: 在dispose方法中停止扫描,以确保在组件被销毁时不会继续监听扫描结果。

请注意,实际使用中你可能需要处理更多的边缘情况,比如权限请求被用户拒绝时的用户引导,以及扫描结果的更新处理等。此外,确保在真机上进行测试,因为模拟器通常不支持蓝牙功能。

回到顶部