簡體   English   中英

將數據從 Flutter 藍牙應用程序發送到 Firestore

[英]Send data from Flutter Bluetooth app to Firestore

https://i.stack.imgur.com/9k5MB.png

我是 flutter 的新手,我正在 flutter 中開發社交距離應用程序。 我想將發現的藍牙設備的 uuid 推送到 Firestore,有人可以幫我這樣做嗎

我可以在我的應用程序中打印出發現的設備,並且可以獲得 rssi uuid txpower,但隨着 rssi 不斷變化並且掃描不斷發生,設備被多次推送到 Firestore,我希望 UUID 只被推送到 firestore 一次。

附近.dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:beacon_broadcast/beacon_broadcast.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'homescreen.dart';
import 'material.dart';

class BlueApp extends StatefulWidget {
  @override
  _BlueAppState createState() => _BlueAppState();
}


BeaconBroadcast beaconBroadcast = BeaconBroadcast();

BeaconStatus _isTransmissionSupported;
bool _isAdvertising = false;
StreamSubscription<bool> _isAdvertisingSubscription;
final databaseReference=Firestore.instance;
String UUID = "3E4D7TJ9008";
void createRecord(String usid) async {
  await databaseReference.collection("users")
      .document("1")
      .setData({
    'uuid':usid
  });
  print('senddddddddingggggg');
  DocumentReference ref = await databaseReference.collection("users")
      .add({
    'uuid':usid
  });
  print(ref.documentID);
}
class _BlueAppState extends State<BlueApp> {

  static const UUID = '39ED98FF';
  static const MAJOR_ID = 1;
  static const MINOR_ID = 100;
  static const TRANSMISSION_POWER = -59;
  static const IDENTIFIER = 'com.example.myDeviceRegion';
  static const LAYOUT = BeaconBroadcast.ALTBEACON_LAYOUT;
  static const MANUFACTURER_ID = 0x0118;
  void initState() {
    // TODO: implement initState
    super.initState();

    FlutterBlue.instance.state.listen((state) {
      print("im in the init");
      print(state);
      if (state == BluetoothState.off) {
        print("bluetooth is off");

      } else if (state == BluetoothState.on) {
        print("bluethooth on");
        //print(device.id);
      }
      print("printing  eacon");
    });
    beaconBroadcast.checkTransmissionSupported().then((isTransmissionSupported) {
      setState(() {
        _isTransmissionSupported = isTransmissionSupported;
        print(_isTransmissionSupported);
      });
    });

    _isAdvertisingSubscription =
        beaconBroadcast.getAdvertisingStateChange().listen((isAdvertising) {
          setState(() {
            _isAdvertising = isAdvertising;
          });
        });
    beaconBroadcast
        .setUUID(UUID)
        .setMajorId(MAJOR_ID)
        .setMinorId(MINOR_ID)
        .setTransmissionPower(-59)
        .setIdentifier(IDENTIFIER)
        .setLayout(LAYOUT)
        .setManufacturerId(MANUFACTURER_ID)
        .start();
    if(_isAdvertising==true){
      print('Beacon started Advertising');
    }
  }




  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      color: Colors.lightBlue,
      home: StreamBuilder<BluetoothState>(
          stream: FlutterBlue.instance.state,
          initialData: BluetoothState.turningOn,
          builder: (c, snapshot) {
            final state = snapshot.data;
            print(state);
            if (state == BluetoothState.on) {
              print("BlueTooth is on");
              return FindDevicesScreen();
            }
            print("BlueTooth is off");
            return BluetoothOffScreen(state: state);
          }),
    );
  }
}

class BluetoothOffScreen extends StatelessWidget {

  const BluetoothOffScreen({Key key, this.state}) : super(key: key);

  final BluetoothState state;


  @override
  Widget build(BuildContext context) {
    _BlueAppState a=new _BlueAppState();
    createRecord(UUID);
    return Scaffold(
      backgroundColor: Colors.lightBlue,
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Icon(
              Icons.bluetooth_disabled,
              size: 200.0,
              color: Colors.white54,
            ),
            Text(
              'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
              style: Theme.of(context)
                  .primaryTextTheme
                  .subhead
                  .copyWith(color: Colors.white),
            ),


          ],
        ),
      ),
    );
  }
}

class FindDevicesScreen extends StatefulWidget {
  @override
  _FindDevicesScreenState createState() => _FindDevicesScreenState();
}

class _FindDevicesScreenState extends State<FindDevicesScreen> {


  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    FlutterBlue.instance.startScan();
    beaconBroadcast.checkTransmissionSupported().then((isTransmissionSupported) {
      setState(() {
        _isTransmissionSupported = isTransmissionSupported;
        print(_isTransmissionSupported);
      });
    });

    _isAdvertisingSubscription =
        beaconBroadcast.getAdvertisingStateChange().listen((isAdvertising) {
          setState(() {
            _isAdvertising = isAdvertising;
          });
        });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Find Devices'),
      ),
      body: RefreshIndicator(
        onRefresh: () =>
            FlutterBlue.instance.startScan(),
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[

              StreamBuilder<List<BluetoothDevice>>(
                stream: Stream.periodic(Duration(seconds: 4))
                    .asyncMap((_) => FlutterBlue.instance.connectedDevices),
                initialData: [],
                builder: (c, snapshot) => Column(
                  children: snapshot.data
                      .map((d) => ListTile(
                    title: Text(d.name),
                    subtitle: Text(d.id.toString()),
                    trailing: StreamBuilder<BluetoothDeviceState>(
                      stream: d.state,
                      initialData: BluetoothDeviceState.disconnected,
                      builder: (c, snapshot) {
                        print('entering if');
                        if (true) {
                             print('id----------------------------------------------did');
                        }
                        return Text(snapshot.data.toString());
                      },
                    ),
                  ))
                      .toList(),
                ),
              ),
              StreamBuilder<List<ScanResult>>(
                stream: FlutterBlue.instance.scanResults,
                initialData: [],
                builder: (c, snapshot) => Column(
                  children: snapshot.data
                      .map(
                        (r) => Card(
                      child: ScanResultTile(
                        result: r,
                        onTap: () => Navigator.of(context)
                            .push(MaterialPageRoute(builder: (context) {

                          return null;
                        })),
                      ),
                    ),
                  )
                      .toList(),
                ),
              ),
            ],
          ),
        ),
      ),

    );
  }
}

材料.dart

import 'package:flutter_blue/flutter_blue.dart';
import 'nearby.dart';
class ScanResultTile extends StatelessWidget {
  const ScanResultTile({Key key, this.result, this.onTap}) : super(key: key);

  final ScanResult result;
  final VoidCallback onTap;

  Widget _buildTitle(BuildContext context) {
    if (result.device.name.length > 0) {
      return Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[

          Text(
            result.device.name,
            overflow: TextOverflow.ellipsis,
          ),
          Text(
            result.device.id.toString(),
            style: Theme.of(context).textTheme.caption,
          ),
//          _buildAdvRow(
//              context, 'Distance of the device',(result.rssi!=null && result.advertisementData.txPowerLevel!=null)?"${getDistance(result.rssi,result.advertisementData.txPowerLevel)}":"N/A" ),
        ],
      );
    } else {
      return Text(result.device.id.toString());
    }
  }

  Widget _buildAdvRow(BuildContext context, String title, String value) {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(title, style: Theme.of(context).textTheme.caption),
          SizedBox(
            width: 12.0,
          ),
          Expanded(
            child: Text(
              value,
              style: Theme.of(context)
                  .textTheme
                  .caption
                  .apply(color: Colors.black),
              softWrap: true,
            ),
          ),
        ],
      ),
    );
  }

  String getNiceHexArray(List<int> bytes) {
    return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
        .toUpperCase();
  }


  int getDistance(int rssi, int txPower) {
    print("rssi");
    print(rssi);
    return 10 ^ ((txPower - rssi) / (10 * 2)).round();
  }

  @override
  Widget build(BuildContext context) {
    print("rssi");
    print(result.rssi);
    print("Transmit power");
    print(result.advertisementData.txPowerLevel);
   // print(result.device.name);
    print(result);
//    if((getDistance(result.rssi,result.advertisementData.txPowerLevel))<=2)
//    {
//      createRecord(result.advertisementData.serviceUuids.iterator.moveNext().toString());
//    }
    return ExpansionTile(
      title: _buildTitle(context),
      leading: Column(
        children: <Widget>[

          Text("Tap for more...",style: TextStyle(fontSize: 10.0,color: Colors.lightBlueAccent),)
        ],
      ),

      children: <Widget>[
        _buildAdvRow(
            context, 'Distance of the device',(result.rssi!=null && result.advertisementData.txPowerLevel!=null)?"${getDistance(result.rssi,result.advertisementData.txPowerLevel)}":"N/A" ),
        _buildAdvRow(context, 'Tx Power Level',
            '${result.advertisementData.txPowerLevel ?? 'N/A'}'),

        _buildAdvRow(
            context,
            'Service UUIDs',
            (result.advertisementData.serviceUuids.isNotEmpty)? result.advertisementData.serviceUuids.join(', ').toUpperCase(): 'N/A'),
      ],
    );
  }
}

class ServiceTile extends StatelessWidget {
  final BluetoothService service;
  final List<CharacteristicTile> characteristicTiles;

  const ServiceTile({Key key, this.service, this.characteristicTiles})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    if (characteristicTiles.length > 0) {
      return ExpansionTile(
        title: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text('Service'),
            Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
                style: Theme.of(context)
                    .textTheme
                    .body1
                    .copyWith(color: Theme.of(context).textTheme.caption.color))
          ],
        ),
        children: characteristicTiles,
      );
    } else {
      return ListTile(
        title: Text('Service'),
        subtitle:
        Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
      );
    }
  }
}

class CharacteristicTile extends StatelessWidget {
  final BluetoothCharacteristic characteristic;
  final List<DescriptorTile> descriptorTiles;
  final VoidCallback onReadPressed;
  final VoidCallback onWritePressed;
  final VoidCallback onNotificationPressed;

  const CharacteristicTile(
      {Key key,
        this.characteristic,
        this.descriptorTiles,
        this.onReadPressed,
        this.onWritePressed,
        this.onNotificationPressed})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<int>>(
      stream: characteristic.value,
      initialData: characteristic.lastValue,
      builder: (c, snapshot) {
        final value = snapshot.data;
        return ExpansionTile(
          title: ListTile(
            title: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text('Characteristic'),
                Text(
                    '0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
                    style: Theme.of(context).textTheme.body1.copyWith(
                        color: Theme.of(context).textTheme.caption.color))
              ],
            ),
            subtitle: Text(value.toString()),
            contentPadding: EdgeInsets.all(0.0),
          ),
          children: descriptorTiles,
        );
      },
    );
  }
}

class DescriptorTile extends StatelessWidget {
  final BluetoothDescriptor descriptor;
  final VoidCallback onReadPressed;
  final VoidCallback onWritePressed;

  const DescriptorTile(
      {Key key, this.descriptor, this.onReadPressed, this.onWritePressed})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text('Descriptor'),
          Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
              style: Theme.of(context)
                  .textTheme
                  .body1
                  .copyWith(color: Theme.of(context).textTheme.caption.color))
        ],
      ),
      subtitle: StreamBuilder<List<int>>(
        stream: descriptor.value,
        initialData: descriptor.lastValue,
        builder: (c, snapshot) => Text(snapshot.data.toString()),
      ),
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          IconButton(
            icon: Icon(
              Icons.file_download,
              color: Theme.of(context).iconTheme.color.withOpacity(0.5),
            ),
            onPressed: onReadPressed,
          ),
          IconButton(
            icon: Icon(
              Icons.file_upload,
              color: Theme.of(context).iconTheme.color.withOpacity(0.5),
            ),
            onPressed: onWritePressed,
          )
        ],
      ),
    );
  }
}

class AdapterStateTile extends StatelessWidget {
  const AdapterStateTile({Key key, @required this.state}) : super(key: key);

  final BluetoothState state;

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.redAccent,
      child: ListTile(
        title: Text(
          'Bluetooth adapter is ${state.toString().substring(15)}',
          style: Theme.of(context).primaryTextTheme.subhead,
        ),
        trailing: Icon(
          Icons.error,
          color: Theme.of(context).primaryTextTheme.subhead.color,
        ),
      ),
    );
  }
}```


為了避免添加額外的時間,有兩種選擇。

備選方案 1:

  • 索引 uuid 字段,因為這將允許您查詢此字段
  • 在使用此 uuid 編寫文檔查詢之前
  • 僅在響應為空時寫入

這種替代方案的優點是無需對 firestore 進行更改,並且對代碼進行最小更改,但缺點是每次寫入嘗試都將在讀取之前進行,並且讀取需要付費。

備選方案 2:

更改 Firestore 上的數據結構,使 UUID 為文檔 ID

這樣做的好處是寫入不會先於讀取,因此它的 Firestore 成本更便宜。 但是,它需要更多的代碼版本,並且需要更改數據結構。

對於代碼,主要變化在於以下方法:

void createRecord(String usid) async {
  await databaseReference.collection("users")
      .document(usid)
      .setData({
    'uuid':usid
  });
  print('senddddddddingggggg');
  DocumentReference ref = await databaseReference.collection("users")
      .document(usid)
      .setData({
    'uuid':usid
  });
  print(ref.documentID);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM