首页 > 解决方案 > 用于扫描信标的 iOS 上的 Flutter 蓝牙检测

问题描述

我正在开发一个应用程序,我需要扫描 iBeacons。我使用了 beacons_plugin ( https://pub.dev/packages/beacons_plugin ),它在 Android 和 iOS 上运行良好。但自 2.0.0 以来,它不再适用于 iOS。我试图理解为什么,但我无法理解。请问有什么帮助吗?

我的 info.plist :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>MiLo</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>$(FLUTTER_BUILD_NAME)</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>$(FLUTTER_BUILD_NUMBER)</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>App needs location permissions to scan nearby beacons.</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>App needs location permissions to scan nearby beacons.</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>App needs location permissions to scan nearby beacons.</string>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsLocalNetworking</key>
        <true/>
    </dict>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
</dict>
</plist>

我使用插件的小部件:


// All packages needed by the widget
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io' show Platform;
import 'dart:convert';
import 'package:beacons_plugin/beacons_plugin.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_application_1/widgets/beacon_card.dart';

class BeaconDetection extends StatefulWidget {
  // Declaration of the variables needed by the widget 1/2
  final townId;
  final fontsizeValue;
  final languageId;
  final wordMessageBeforeScan;
  final wordButtonLaunchScan;
  final wordButtonStopScan;
  // Declaration of the variables needed by the widget 2/2
  BeaconDetection(
      {this.townId,
      this.fontsizeValue,
      this.languageId,
      this.wordMessageBeforeScan,
      this.wordButtonLaunchScan,
      this.wordButtonStopScan});
  @override
  _BeaconDetectionState createState() => _BeaconDetectionState();
}

class _BeaconDetectionState extends State<BeaconDetection> {
  bool isRunning = false;
  String _beaconUuid = '';
  double _beaconDistance = 0;
  String _beaconDetectedUuid = 'null';
  double _beaconDetectedDistance = 0;

  final StreamController<String> beaconEventsController =
      StreamController<String>.broadcast();
  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  @override
  void dispose() {
    beaconEventsController.close();
    super.dispose();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    if (Platform.isAndroid) {
      // Prominent disclosure
      await BeaconsPlugin.setDisclosureDialogMessage(
          title: "Need Location Permission",
          message: "This app collects location data to work with beacons.");
      // Only in case, you want the dialog to be shown again. By Default, dialog will never be shown if permissions are granted.
      // await BeaconsPlugin.clearDisclosureDialogShowFlag(false);
    }

    BeaconsPlugin.listenToBeacons(beaconEventsController);

    // Extraction of the name and the uuid of the beacons of the chosen city from the firebase
    FirebaseFirestore.instance
        .collection('towns/${widget.townId}/beacons')
        .snapshots()
        .listen((event) {
      event.docs.forEach((element) async {
        if (element['name'].isNotEmpty == true &&
            element['uuid'].isNotEmpty == true &&
            element['visibility'] == true) {
          await BeaconsPlugin.addRegion(element['name'], element['uuid']);
        }
      });
    });

    // When listening the data from beacons detected
    beaconEventsController.stream.listen(
        (data) {
          if (data.isNotEmpty) {
            setState(() {
              Map _beaconScanned = jsonDecode(data);
              _beaconUuid = _beaconScanned['uuid'];
              _beaconDistance = double.parse(_beaconScanned['distance']);
            });
            // print("Beacons DataReceived: " + data);
            FirebaseFirestore.instance
                .collection('towns/${widget.townId}/beacons')
                .where('uuid', isEqualTo: _beaconUuid)
                .snapshots()
                .listen((event) {
              event.docs.forEach((element) {
                if (_beaconUuid == element['uuid'] &&
                    element['visibility'] == true &&
                    _beaconDistance <= element['distance']) {
                  print('Beacon: $_beaconUuid | Distance: $_beaconDistance');
                  _beaconDetectedUuid = _beaconUuid;
                  _beaconDetectedDistance = _beaconDistance;
                }
              });
            });
          }
        },
        onDone: () {},
        onError: (error) {
          print("Error: $error");
        });
    await BeaconsPlugin.runInBackground(
        isRunning); // Send 'true' to run in background
    if (Platform.isAndroid) {
      BeaconsPlugin.channel.setMethodCallHandler((call) async {
        if (call.method == 'scannerReady') {
          await BeaconsPlugin.startMonitoring();
          setState(() {
            isRunning = true;
          });
        }
      });
    } else if (Platform.isIOS) {
      await BeaconsPlugin.startMonitoring();
      setState(() {
        isRunning = true;
      });
    }
    if (!mounted) return;
  }

  Widget build(BuildContext context) {
    return Column(
      children: [
        StreamBuilder<QuerySnapshot>(
            stream: FirebaseFirestore.instance
                .collection('towns/${widget.townId}/beacons')
                .where('uuid', isEqualTo: _beaconDetectedUuid)
                .snapshots(),
            builder: (ctx, snapshot) {
              if (snapshot.connectionState ==
                  ConnectionState.waiting) // Initialisation state
                return Container(
                  margin: const EdgeInsets.all(10),
                  height: 314, // Beacon card's size
                  child: Center(child: CircularProgressIndicator()),
                );
              if (_beaconDetectedUuid == 'null' &&
                  isRunning == false) // Before first scan State
                return Container(
                  margin: const EdgeInsets.all(10),
                  height: 314, // Beacon card's size
                  child: Center(child: Text(widget.wordMessageBeforeScan)),
                );
              if (_beaconDetectedUuid == 'null' &&
                  isRunning == true) // Launch first scan state
                return Container(
                  margin: const EdgeInsets.all(10),
                  height: 314, // Beacon card's size
                  child: Center(
                    child: CircularProgressIndicator(),
                  ),
                );
              final beacons = snapshot.data!.docs;
              return BeaconCard( // When the detection is done => Display the widget 'BeaconCard' with the following data
                title: beacons[0]['title'],
                monument: beacons[0]['monument'],
                image: beacons[0]['image'],
                duration: beacons[0]['duration'],
                distance: _beaconDetectedDistance,
                townId: widget.townId,
                uuid: beacons[0]['uuid'],
                fontsizeValue: widget.fontsizeValue,
                languageId: widget.languageId,
              );
            }),
        isRunning
            ? FloatingActionButton.extended( // If 'isRunning' is true => Display a button to stop the scan
                icon: Icon(Icons.bluetooth_disabled),
                label: Text(widget.wordButtonStopScan),
                backgroundColor: Colors.red,
                onPressed: () async {
                  await BeaconsPlugin.stopMonitoring();
                  setState(() {
                    isRunning = false;
                  });
                },
              )
            : FloatingActionButton.extended( // If 'isRunning' is false => Display a button to start the scan
                icon: Icon(Icons.bluetooth),
                label: Text(widget.wordButtonLaunchScan),
                backgroundColor: Theme.of(context).primaryColor,
                onPressed: () async {
                  initPlatformState();
                  await BeaconsPlugin.startMonitoring();
                  setState(() {
                    isRunning = true;
                    _beaconDetectedUuid = 'null';
                  });
                },
              ),
        SizedBox(
          height: 16,
        )
      ],
    );
  }
}

非常感谢

标签: iosflutterbluetoothibeacon

解决方案


推荐阅读