Flutter地理位置服务插件geoflutterfire2的使用

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

Flutter地理位置服务插件geoflutterfire2的使用

GeoFlutterFire2 🌍

GeoFlutterFire2GeoFlutterFire的修订和更新版本。它是一个开源库,允许基于地理位存储和查询一组键。其核心功能是在Firebase Firestore数据库中存储位置并用字符串键表示。主要优势在于可以实时检索特定地理区域内的键。

开始使用

确保在Flutter项目中添加GeoFlutterFire2作为依赖项。

dependencies:
  geoflutterfire2: <latest-version>

或直接引用git仓库:

dependencies:
  geoflutterfire2:
    git: git://github.com/beerstorm-net/GeoFlutterFire2.git

然后运行flutter pub get或在IntelliJ中更新包。

示例代码

以下是一个完整的示例应用程序,展示了如何初始化、写入和查询地理数据。

初始化

首先需要一个配置了Firestore的Firebase项目。

import 'package:geoflutterfire2/geoflutterfire2.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

// 初始化 Firestore 和 GeoFlutterFire
final geo = GeoFlutterFire();
final _firestore = FirebaseFirestore.instance;

写入地理数据

创建一个地理点并将其添加到Firestore文档中。

GeoFirePoint myLocation = geo.point(latitude: 12.960632, longitude: 77.641603);

_firestore.collection('locations').add({
  'name': 'random name', 
  'position': myLocation.data
});

查询地理数据

创建一个地理点并查询距离该点50公里范围内的所有文档。

GeoFirePoint center = geo.point(latitude: 12.960632, longitude: 77.641603);
var collectionReference = _firestore.collection('locations');

double radius = 50;
String field = 'position';

Stream<List<DocumentSnapshot>> stream = geo.collection(collectionRef: collectionReference)
                                        .within(center: center, radius: radius, field: field);

stream.listen((List<DocumentSnapshot> documentList) {
  // 处理查询结果
});

完整示例应用

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:geoflutterfire2/geoflutterfire2.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:rxdart/rxdart.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MaterialApp(
    title: 'Geo Flutter Fire example',
    home: MyApp(),
    debugShowCheckedModeBanner: false,
  ));
}

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

class _MyAppState extends State<MyApp> {
  GoogleMapController _mapController;
  TextEditingController _latitudeController, _longitudeController;

  final _firestore = FirebaseFirestore.instance;
  GeoFlutterFire geo;
  Stream<List<DocumentSnapshot>> stream;
  final radius = BehaviorSubject<double>.seeded(1.0);
  Map<MarkerId, Marker> markers = {};

  @override
  void initState() {
    super.initState();
    _latitudeController = TextEditingController();
    _longitudeController = TextEditingController();

    geo = GeoFlutterFire();
    GeoFirePoint center = geo.point(latitude: 12.960632, longitude: 77.641603);
    stream = radius.switchMap((rad) {
      var collectionReference = _firestore.collection('locations');
      return geo.collection(collectionRef: collectionReference).within(
          center: center, radius: rad, field: 'position', strictMode: true);
    });
  }

  @override
  void dispose() {
    _latitudeController.dispose();
    _longitudeController.dispose();
    radius.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('GeoFlutterFire'),
          actions: [
            IconButton(
              onPressed: _mapController == null ? null : () => _showHome(),
              icon: Icon(Icons.home),
            )
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return StreamTestWidget();
            }));
          },
          child: Icon(Icons.navigate_next),
        ),
        body: Container(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Center(
                child: Card(
                  elevation: 4,
                  margin: EdgeInsets.symmetric(vertical: 8),
                  child: SizedBox(
                    width: MediaQuery.of(context).size.width - 30,
                    height: MediaQuery.of(context).size.height * (1 / 3),
                    child: GoogleMap(
                      onMapCreated: _onMapCreated,
                      initialCameraPosition: const CameraPosition(
                        target: LatLng(12.960632, 77.641603),
                        zoom: 15.0,
                      ),
                      markers: Set<Marker>.of(markers.values),
                    ),
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(top: 8.0),
                child: Slider(
                  min: 1,
                  max: 200,
                  divisions: 4,
                  value: _value,
                  label: _label,
                  activeColor: Colors.blue,
                  inactiveColor: Colors.blue.withOpacity(0.2),
                  onChanged: (double value) => changed(value),
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Container(
                    width: 100,
                    child: TextField(
                      controller: _latitudeController,
                      keyboardType: TextInputType.number,
                      decoration: InputDecoration(
                          labelText: 'lat',
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(8),
                          )),
                    ),
                  ),
                  Container(
                    width: 100,
                    child: TextField(
                      controller: _longitudeController,
                      keyboardType: TextInputType.number,
                      decoration: InputDecoration(
                          labelText: 'lng',
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(8),
                          )),
                    ),
                  ),
                  MaterialButton(
                    color: Colors.blue,
                    onPressed: () {
                      final lat = double.parse(_latitudeController.text);
                      final lng = double.parse(_longitudeController.text);
                      _addPoint(lat, lng);
                    },
                    child: const Text(
                      'ADD',
                      style: TextStyle(color: Colors.white),
                    ),
                  )
                ],
              ),
              MaterialButton(
                color: Colors.amber,
                child: const Text(
                  'Add nested ',
                  style: TextStyle(color: Colors.white),
                ),
                onPressed: () {
                  final lat = double.parse(_latitudeController.text);
                  final lng = double.parse(_longitudeController.text);
                  _addNestedPoint(lat, lng);
                },
              )
            ],
          ),
        ),
      ),
    );
  }

  void _onMapCreated(GoogleMapController controller) {
    setState(() {
      _mapController = controller;
      stream.listen((List<DocumentSnapshot> documentList) {
        _updateMarkers(documentList);
      });
    });
  }

  void _showHome() {
    _mapController.animateCamera(CameraUpdate.newCameraPosition(
      const CameraPosition(
        target: LatLng(12.960632, 77.641603),
        zoom: 15.0,
      ),
    ));
  }

  void _addPoint(double lat, double lng) {
    GeoFirePoint geoFirePoint = geo.point(latitude: lat, longitude: lng);
    _firestore.collection('locations')
        .add({'name': 'random name', 'position': geoFirePoint.data})
        .then((_) {
      print('added ${geoFirePoint.hash} successfully');
    });
  }

  void _addNestedPoint(double lat, double lng) {
    GeoFirePoint geoFirePoint = geo.point(latitude: lat, longitude: lng);
    _firestore.collection('nestedLocations').add({
      'name': 'random name',
      'address': {'location': {'position': geoFirePoint.data}}
    }).then((_) {
      print('added ${geoFirePoint.hash} successfully');
    });
  }

  void _addMarker(double lat, double lng) {
    final id = MarkerId(lat.toString() + lng.toString());
    final _marker = Marker(
      markerId: id,
      position: LatLng(lat, lng),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet),
      infoWindow: InfoWindow(title: 'latLng', snippet: '$lat,$lng'),
    );
    setState(() {
      markers[id] = _marker;
    });
  }

  void _updateMarkers(List<DocumentSnapshot> documentList) {
    documentList.forEach((DocumentSnapshot document) {
      Map<String, dynamic> snapData = document.data();
      final GeoPoint point = snapData['position']['geopoint'];
      _addMarker(point.latitude, point.longitude);
    });
  }

  double _value = 20.0;
  String _label = '';

  changed(value) {
    setState(() {
      _value = value;
      _label = '${_value.toInt().toString()} kms';
      markers.clear();
    });
    radius.add(value);
  }
}

这个示例展示了如何使用GeoFlutterFire2进行地理位置的写入和查询,并将结果可视化在Google地图上。希望这能帮助你更好地理解和使用GeoFlutterFire2插件。


更多关于Flutter地理位置服务插件geoflutterfire2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地理位置服务插件geoflutterfire2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用geoflutterfire2插件的示例代码。geoflutterfire2是一个用于地理位置查询的Flutter插件,它基于Firebase Realtime Database,允许你根据用户的地理位置进行地理围栏(Geofencing)和查询。

前提条件

  1. 确保你已经创建了Firebase项目,并在项目中启用了Realtime Database。
  2. 在你的Flutter项目中添加geoflutterfire2firebase_database依赖。

添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^1.10.0 # 确保版本是最新的
  firebase_database: ^9.0.4 # 确保版本是最新的
  geoflutterfire2: ^2.2.0+2 # 确保版本是最新的

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

配置Firebase

确保你已经按照Firebase的官方文档配置了你的Flutter项目,包括添加google-services.json(Android)和GoogleService-Info.plist(iOS)文件。

示例代码

以下是一个完整的示例,展示如何使用geoflutterfire2来设置一个地理围栏并查询附近的位置。

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:geoflutterfire2/geoflutterfire2.dart';
import 'package:geolocator/geolocator.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: GeoFenceScreen(),
    );
  }
}

class GeoFenceScreen extends StatefulWidget {
  @override
  _GeoFenceScreenState createState() => _GeoFenceScreenState();
}

class _GeoFenceScreenState extends State<GeoFenceScreen> {
  DatabaseReference _databaseReference = FirebaseDatabase.instance.reference();
  GeoFirePoint? _centerGeoFirePoint;
  double _radius = 1.0; // in kilometers

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

  Future<void> _getCurrentLocation() async {
    Position position = await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high,
    );
    double latitude = position.latitude;
    double longitude = position.longitude;

    setState(() {
      _centerGeoFirePoint = geo.point(latitude: latitude, longitude: longitude);
    });

    _setupGeoFence();
  }

  void _setupGeoFence() {
    if (_centerGeoFirePoint != null) {
      GeoFire geoFire = GeoFire(databaseRef: _databaseReference.child('locations'));
      GeoQuery geoQuery = geoFire.collection(center: _centerGeoFirePoint!, radius: _radius, field: 'g');

      geoQuery.onKeyEntered.listen((key) {
        print("Key $key entered the geofence!");
      });

      geoQuery.onKeyExited.listen((key) {
        print("Key $key exited the geofence!");
      });

      geoQuery.onKeyMoved.listen((key) {
        print("Key $key moved within the geofence!");
      });

      geoQuery.onKeyHere.listen((key) {
        print("Key $key is at the center of the geofence!");
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GeoFlutterFire2 Example'),
      ),
      body: Center(
        child: _centerGeoFirePoint == null
            ? CircularProgressIndicator()
            : Text('GeoFence Set at: $_centerGeoFirePoint'),
      ),
    );
  }
}

解释

  1. 初始化Firebase:在main函数中,我们初始化了Firebase应用。
  2. 获取当前位置:在_getCurrentLocation函数中,我们使用geolocator插件获取当前设备的位置。
  3. 设置地理围栏:在_setupGeoFence函数中,我们使用GeoFireGeoQuery来设置一个地理围栏,并监听进入、退出、移动和位于中心的事件。
  4. UI展示:在UI中,我们显示当前设置的地理围栏的中心点。

注意事项

  • 确保你的Firebase Realtime Database规则允许读写操作。
  • 在实际应用中,处理位置数据时请考虑用户隐私和数据安全。

这个示例代码展示了基本的地理围栏设置和事件监听,你可以根据实际需求进一步扩展功能。

回到顶部