Flutter地理位置服务插件geoflutterfire2的使用
Flutter地理位置服务插件geoflutterfire2的使用
GeoFlutterFire2 🌍
GeoFlutterFire2
是GeoFlutterFire
的修订和更新版本。它是一个开源库,允许基于地理位存储和查询一组键。其核心功能是在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
更多关于Flutter地理位置服务插件geoflutterfire2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用geoflutterfire2
插件的示例代码。geoflutterfire2
是一个用于地理位置查询的Flutter插件,它基于Firebase Realtime Database,允许你根据用户的地理位置进行地理围栏(Geofencing)和查询。
前提条件
- 确保你已经创建了Firebase项目,并在项目中启用了Realtime Database。
- 在你的Flutter项目中添加
geoflutterfire2
和firebase_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'),
),
);
}
}
解释
- 初始化Firebase:在
main
函数中,我们初始化了Firebase应用。 - 获取当前位置:在
_getCurrentLocation
函数中,我们使用geolocator
插件获取当前设备的位置。 - 设置地理围栏:在
_setupGeoFence
函数中,我们使用GeoFire
和GeoQuery
来设置一个地理围栏,并监听进入、退出、移动和位于中心的事件。 - UI展示:在UI中,我们显示当前设置的地理围栏的中心点。
注意事项
- 确保你的Firebase Realtime Database规则允许读写操作。
- 在实际应用中,处理位置数据时请考虑用户隐私和数据安全。
这个示例代码展示了基本的地理围栏设置和事件监听,你可以根据实际需求进一步扩展功能。