Flutter地理围栏插件geofencing_api的使用
Flutter地理围栏插件geofencing_api的使用
插件概述
geofencing_api
插件用于实现圆形和多边形地理围栏服务。此插件不依赖平台原生的地理围栏API,因此不能保证电池效率。然而,它可以提供更准确和实时的地理围栏服务,只要应用程序处于活动状态。
注意:该插件不使用平台提供的地理围栏API实现,因此无法保证电池效率。相反,当应用程序处于活动状态时,它可以通过导航位置来提供更准确和实时的地理围栏服务。
主要特性
- 可创建圆形类型的地理围栏。
- 可创建多边形类型的地理围栏。
- 可实时监听地理围栏状态变化。
- 可实时监听位置变化。
- 可请求或检查位置权限。
入门指南
添加依赖
在pubspec.yaml
文件中添加geofencing_api
作为依赖项:
dependencies:
geofencing_api: ^2.0.0
平台特定配置
Android
打开AndroidManifest.xml
文件,并在<manifest>
和<application>
标签之间声明权限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
如果需要在后台运行地理围栏服务,还需声明以下权限:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
iOS
打开ios/Runner/Info.plist
文件,并在<dict>
标签内声明描述:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to collect location data.</string>
若需在后台收集位置数据,则还需添加以下描述:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Used to collect location data in the background.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Used to collect location data in thebackground.</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
</array>
使用方法
请求位置权限
开始地理围栏服务前,必须确保已获得适当的位置权限(always
或whileInUse
)。以下是示例代码:
Future<bool> requestLocationPermission({bool background = false}) async {
if (!await Geofencing.instance.isLocationServicesEnabled) {
return false;
}
LocationPermission permission = await Geofencing.instance.getLocationPermission();
if (permission == LocationPermission.denied) {
permission = await Geofencing.instance.requestLocationPermission();
}
if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
return false;
}
if (kIsWeb || kIsWasm) {
return true;
}
if (Platform.isAndroid && background && permission == LocationPermission.whileInUse) {
permission = await Geofencing.instance.requestLocationPermission();
if (permission != LocationPermission.always) {
return false;
}
}
return true;
}
设置地理围栏服务
设置地理围栏服务时可以配置以下参数:
interval
: 更新地理围栏状态的时间间隔,默认为5000毫秒。accuracy
: 地理围栏服务的精度,默认为100米。statusChangeDelay
: 状态变化延迟时间,默认为10000毫秒。allowsMockLocation
: 是否允许模拟位置,默认为false
。printsDebugLog
: 是否打印调试日志,默认为true
。
void setupGeofencing() {
Geofencing.instance.setup(
interval: 5000,
accuracy: 100,
statusChangeDelay: 10000,
allowsMockLocation: false,
printsDebugLog: true,
);
}
创建地理围栏区域
创建地理围栏区域时可以使用.circular
或.polygon
构造函数:
final Set<GeofenceRegion> _regions = {
GeofenceRegion.circular(
id: 'circular_region',
data: {
'name': 'National Museum of Korea',
},
center: const LatLng(37.523085, 126.979619),
radius: 250,
loiteringDelay: 60 * 1000,
),
GeofenceRegion.polygon(
id: 'polygon_region',
data: {
'name': 'Gyeongbokgung Palace',
},
polygon: [
const LatLng(37.583696, 126.973739),
const LatLng(37.583441, 126.979361),
const LatLng(37.582506, 126.980198),
const LatLng(37.579054, 126.979490),
const LatLng(37.576112, 126.979061),
const LatLng(37.576503, 126.974126),
const LatLng(37.580959, 126.973568),
],
loiteringDelay: 60 * 1000,
),
};
启动地理围栏服务
启动地理围栏服务时可以添加地理围栏区域并设置监听器:
void startGeofencing() async {
Geofencing.instance.addGeofenceStatusChangedListener(_onGeofenceStatusChanged);
Geofencing.instance.addGeofenceErrorCallbackListener(_onGeofenceError);
await Geofencing.instance.start(regions: _regions);
}
Future<void> _onGeofenceStatusChanged(
GeofenceRegion geofenceRegion,
GeofenceStatus geofenceStatus,
Location location,
) async {
final String regionId = geofenceRegion.id;
final String statusName = geofenceStatus.name;
print('region(id: $regionId) $statusName');
}
void _onGeofenceError(Object error, StackTrace stackTrace) {
print('error: $error\n$stackTrace');
}
增删地理围栏区域
即使服务已启动,也可以动态增删地理围栏区域:
void addRegions() {
Geofencing.instance.addRegion(GeofenceRegion);
Geofencing.instance.addRegions(Set<GeofenceRegion>);
}
void removeRegions() {
Geofencing.instance.removeRegion(GeofenceRegion);
Geofencing.instance.removeRegions(Set<GeofenceRegion>);
Geofencing.instance.removeRegionById(String);
Geofencing.instance.clearAllRegions();
}
暂停与恢复服务
暂停或恢复地理围栏服务:
void pauseGeofencing() {
Geofencing.instance.pause();
}
void resumeGeofencing() {
Geofencing.instance.resume();
}
停止服务
停止地理围栏服务时可以选择是否保留已添加的区域:
void stopGeofencing() async {
Geofencing.instance.removeGeofenceStatusChangedListener(_onGeofenceStatusChanged);
Geofencing.instance.removeGeofenceErrorCallbackListener(_onGeofenceError);
await Geofencing.instance.stop(keepsRegions: true);
}
示例Demo
下面是一个完整的示例Demo,展示了如何结合Google Maps使用geofencing_api
插件。
import 'dart:developer' as dev;
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:geofencing_api/geofencing_api.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as map;
const Color _kEnterColor = Color(0xFF4CAF50);
const Color _kExitColor = Color(0xFFF44336);
const Color _kDwellColor = Color(0xFF9C27B0);
final Set<GeofenceRegion> _regions = {
GeofenceRegion.circular(
id: 'region_1',
data: {
'name': 'National Museum of Korea',
},
center: const LatLng(37.523085, 126.979619),
radius: 250,
loiteringDelay: 60 * 1000,
),
// 更多区域...
};
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: DemoPage(),
),
);
}
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<StatefulWidget> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
Future<bool> _requestLocationPermission({bool background = false}) async {
if (!await Geofencing.instance.isLocationServicesEnabled) {
return false;
}
LocationPermission permission = await Geofencing.instance.getLocationPermission();
if (permission == LocationPermission.denied) {
permission = await Geofencing.instance.requestLocationPermission();
}
if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
return false;
}
if (kIsWeb || kIsWasm) {
return true;
}
if (Platform.isAndroid && background && permission == LocationPermission.whileInUse) {
permission = await Geofencing.instance.requestLocationPermission();
if (permission != LocationPermission.always) {
return false;
}
}
return true;
}
void _setupGeofencing() {
try {
Geofencing.instance.setup(
interval: 5 * 1000,
accuracy: 100,
statusChangeDelay: 10 * 1000,
allowsMockLocation: true,
printsDebugLog: true,
);
} catch (e, s) {
_onError(e, s);
}
}
void _startGeofencing() async {
try {
Geofencing.instance.addGeofenceStatusChangedListener(_onGeofenceStatusChanged);
Geofencing.instance.addGeofenceErrorCallbackListener(_onError);
await Geofencing.instance.start(regions: _regions);
_refreshPage();
} catch (e, s) {
_onError(e, s);
}
}
void _stopGeofencing() async {
try {
Geofencing.instance.removeGeofenceStatusChangedListener(_onGeofenceStatusChanged);
Geofencing.instance.removeGeofenceErrorCallbackListener(_onError);
await Geofencing.instance.stop();
_refreshPage();
} catch (e, s) {
_onError(e, s);
}
}
void _refreshPage() {
if (mounted) {
setState(() {});
}
}
Future<void> _onGeofenceStatusChanged(
GeofenceRegion geofenceRegion,
GeofenceStatus geofenceStatus,
Location location,
) async {
final String regionId = geofenceRegion.id;
final String statusName = geofenceStatus.name;
dev.log('region(id: $regionId) $statusName');
_refreshPage();
}
void _onError(Object error, StackTrace stackTrace) {
dev.log('error: $error\n$stackTrace');
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await _requestLocationPermission();
_setupGeofencing();
_startGeofencing();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMapView(
regions: Geofencing.instance.regions,
),
);
}
@override
void dispose() {
_stopGeofencing();
super.dispose();
}
}
class GoogleMapView extends StatefulWidget {
const GoogleMapView({
super.key,
required this.regions,
});
final Set<GeofenceRegion> regions;
@override
State<StatefulWidget> createState() => _GoogleMapViewState();
}
class _GoogleMapViewState extends State<GoogleMapView> {
final Set<map.Circle> _circles = {};
final Set<map.Polygon> _polygons = {};
Color _getFillColorByStatus(GeofenceStatus status) {
switch (status) {
case GeofenceStatus.enter:
return _kEnterColor.withOpacity(0.5);
case GeofenceStatus.exit:
return _kExitColor.withOpacity(0.5);
case GeofenceStatus.dwell:
return _kDwellColor.withOpacity(0.5);
}
}
void _updateMapsObject() {
_circles.clear();
_polygons.clear();
for (final GeofenceRegion region in widget.regions) {
if (region is GeofenceCircularRegion) {
_circles.add(map.Circle(
circleId: map.CircleId(region.id),
center: map.LatLng(region.center.latitude, region.center.longitude),
radius: region.radius,
strokeWidth: 2,
strokeColor: Colors.black,
fillColor: _getFillColorByStatus(region.status),
));
continue;
}
if (region is GeofencePolygonRegion) {
_polygons.add(map.Polygon(
polygonId: map.PolygonId(region.id),
points: region.polygon.map((e) => map.LatLng(e.latitude, e.longitude)).toList(),
strokeWidth: 2,
strokeColor: Colors.black,
fillColor: _getFillColorByStatus(region.status),
));
continue;
}
}
}
@override
void initState() {
super.initState();
_updateMapsObject();
}
@override
void didUpdateWidget(covariant GoogleMapView oldWidget) {
_updateMapsObject();
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return map.GoogleMap(
initialCameraPosition: const map.CameraPosition(
target: map.LatLng(37.5479, 126.9904),
zoom: 12.5,
),
circles: _circles,
polygons: _polygons,
myLocationEnabled: true,
myLocationButtonEnabled: true,
);
}
}
以上就是关于geofencing_api
插件的详细介绍及示例代码,希望对您有所帮助!如果您有任何问题或发现任何bug,请通过GitHub Issues反馈给我们。
更多关于Flutter地理围栏插件geofencing_api的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter地理围栏插件geofencing_api的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用geofencing_api
插件来实现地理围栏功能的代码示例。这包括基本的设置、添加地理围栏以及处理进入和离开围栏区域的事件。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加geofencing_api
依赖:
dependencies:
flutter:
sdk: flutter
geofencing_api: ^x.y.z # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
2. 配置Android权限
在android/app/src/main/AndroidManifest.xml
中添加必要的权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.geofencing">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<application
...>
<!-- 其他配置 -->
</application>
</manifest>
3. 初始化插件并添加地理围栏
在你的Dart代码中,你可以按照以下步骤来初始化插件并添加地理围栏:
import 'package:flutter/material.dart';
import 'package:geofencing_api/geofencing_api.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late GeofencingClient _geofencingClient;
@override
void initState() {
super.initState();
_geofencingClient = GeofencingClient();
_initializeGeofencing();
}
Future<void> _initializeGeofencing() async {
// 请求权限(这部分通常在实际应用中会更复杂,包括处理权限请求的结果)
bool hasPermissions = await _requestPermissions();
if (hasPermissions) {
// 添加地理围栏
await _addGeofence();
// 注册围栏事件监听器
_geofencingClient.addGeofenceEventListener(_handleGeofenceEvent);
}
}
Future<bool> _requestPermissions() async {
// 这里应该使用更复杂的权限请求逻辑,比如使用permission_handler插件
// 这里简单返回true以继续演示
return true;
}
Future<void> _addGeofence() async {
final Geofence geofence = Geofence(
requestId: "my_geofence",
circularRegion: CircularRegion(
center: LatLng(37.7853889, -122.4056973),
radius: 100.0, // 半径,单位为米
),
geofenceTransition: GeofenceTransition.ENTER | GeofenceTransition.EXIT,
dwellingTime: Duration(seconds: 30), // 停留时间
notificationResponsiveness: Duration(seconds: 0), // 通知响应性
);
try {
await _geofencingClient.addGeofence(geofence);
print("Geofence added successfully");
} catch (e) {
print("Failed to add geofence: $e");
}
}
void _handleGeofenceEvent(GeofenceEvent event) {
if (event.geofenceTransition == GeofenceTransition.ENTER) {
print("Entered geofence: ${event.requestId}");
} else if (event.geofenceTransition == GeofenceTransition.EXIT) {
print("Exited geofence: ${event.requestId}");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Geofencing Demo'),
),
body: Center(
child: Text('Check the console for geofence events.'),
),
),
);
}
}
4. 处理权限请求
在实际应用中,你需要更细致地处理权限请求,比如使用permission_handler
插件来请求位置权限。这里为了简化,我们直接返回了true
。
5. 运行应用
确保你的设备或模拟器支持地理围栏功能(通常需要Android模拟器配置Google Play服务),然后运行你的Flutter应用。
这个示例展示了如何使用geofencing_api
插件来添加地理围栏并处理进入和离开围栏区域的事件。根据你的具体需求,你可能需要调整代码中的参数和逻辑。