Flutter地理围栏与前台服务插件geofence_foreground_service的使用
Flutter地理围栏与前台服务插件geofence_foreground_service的使用
geofence_foreground_service
geofence_foreground_service
是一个Flutter插件,它使您能够轻松地在Flutter应用中处理地理围栏事件。通过利用原生操作系统的API,在Android上创建一个前台服务以实现电池高效性(使用Geofence和WorkManagerAPI),在iOS上则使用CLLocationManager。
特性
- 支持在前台和后台进行地理围栏 💪
- 地理围栏圆形区域 🗺️
- 地理围栏多边形 🤯 可以使用坐标列表添加地理围栏,系统将计算它们的中心并注册,完整多边形支持正在进行中 🚧
- 通知定制化 🔔 ⚠️Android⚠️ 显示通知是运行前台服务时必须的,您可以自定义显示的内容(标题、内容或图标),默认情况下,插件会显示您的应用图标。
- 通知响应性 ⏱️ ⚠️Android⚠️ 您可以设置Android通知的响应性,详情请参考官方文档
设置
🔧 Android 设置
- 启用MultiDex,具体方法可以查看这里
- 在
AndroidManifest.xml
文件的应用标签内添加服务
<service
android:name="com.f2fk.geofence_foreground_service.GeofenceForegroundService"
android:foregroundServiceType="location">
</service>
- 添加权限
<!--required-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!--至少需要以下权限之一-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- 确保
app/build.gradle
文件中的minSdkVersion
为29+
🔧 iOS 设置
-
导航到Podfile并确保iOS版本为12+
platform :ios, '12.0'
-
确保向Info.plist添加以下权限
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app need your location to provide best feature based on location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app need your location to provide best feature based on location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app need your location to provide best feature based on location</string>
- 打开XCode,启用
Location updates
和Background fetch
功能
示例代码
下面是一个完整的示例代码,演示了如何使用geofence_foreground_service
插件:
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:geofence_foreground_service/constants/geofence_event_type.dart';
import 'package:geofence_foreground_service/exports.dart';
import 'package:geofence_foreground_service/geofence_foreground_service.dart';
import 'package:geofence_foreground_service/models/notification_icon_data.dart';
import 'package:geofence_foreground_service/models/zone.dart';
import 'package:permission_handler/permission_handler.dart';
@pragma('vm:entry-point')
void callbackDispatcher() async {
GeofenceForegroundService().handleTrigger(
backgroundTriggerHandler: (zoneID, triggerType) {
log(zoneID, name: 'zoneID');
if (triggerType == GeofenceEventType.enter) {
log('enter', name: 'triggerType');
} else if (triggerType == GeofenceEventType.exit) {
log('exit', name: 'triggerType');
} else if (triggerType == GeofenceEventType.dwell) {
log('dwell', name: 'triggerType');
} else {
log('unknown', name: 'triggerType');
}
return Future.value(true);
},
);
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static final LatLng _londonCityCenter = LatLng.degree(51.509865, -0.118092);
static final List<LatLng> _timesSquarePolygon = [
LatLng.degree(40.758078, -73.985640),
LatLng.degree(40.757983, -73.985417),
LatLng.degree(40.757881, -73.985493),
LatLng.degree(40.757956, -73.985688),
];
bool _hasServiceStarted = false;
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
await Permission.location.request();
await Permission.locationAlways.request();
await Permission.notification.request();
_hasServiceStarted =
await GeofenceForegroundService().startGeofencingService(
contentTitle: 'Test app is running in the background',
contentText:
'Test app will be running to ensure seamless integration with ops team',
notificationChannelId: 'com.app.geofencing_notifications_channel',
serviceId: 525600,
isInDebugMode: true,
notificationIconData: const NotificationIconData(
resType: ResourceType.mipmap,
resPrefix: ResourcePrefix.ic,
name: 'launcher',
),
callbackDispatcher: callbackDispatcher,
);
log(_hasServiceStarted.toString(), name: 'hasServiceStarted');
}
Future<void> _createLondonGeofence() async {
if (!_hasServiceStarted) {
log('Service has not started yet', name: 'createGeofence');
return;
}
await GeofenceForegroundService().addGeofenceZone(
zone: Zone(
id: 'zone#1_id',
radius: 1000, // measured in meters
coordinates: [_londonCityCenter],
notificationResponsivenessMs: 15 * 1000, // 15 seconds
),
);
}
Future<void> _createTimesSquarePolygonGeofence() async {
if (!_hasServiceStarted) {
log('Service has not started yet', name: 'createGeofence');
return;
}
await GeofenceForegroundService().addGeofenceZone(
zone: Zone(
id: 'zone#2_id',
radius: 10000, // measured in meters
coordinates: _timesSquarePolygon,
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _createLondonGeofence,
child: const Text('Create Circular London Geofence')),
const SizedBox(height: 30),
ElevatedButton(
onPressed: _createTimesSquarePolygonGeofence,
child: const Text('Create Polygon Times Square Geofence')),
],
),
),
),
);
}
}
注意事项
-
处理权限不是此包的一部分,请参考permission_handler插件来授予所需的权限(该插件也在示例中使用)
- location
- locationAlways
- notification
-
callbackDispatcher
方法将在与实际应用程序完全不同的隔离区中运行,因此如果您要在其中处理UI相关代码,则需要使用端口,更多信息请参阅这里
贡献指南
我们欢迎来自社区的贡献。如果您想为这个插件的发展做出贡献,请随时向我们的GitHub仓库提交PR。
更多关于Flutter地理围栏与前台服务插件geofence_foreground_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter地理围栏与前台服务插件geofence_foreground_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中集成并使用geofence_foreground_service
插件来实现地理围栏和前台服务的示例代码。
前提条件
- 确保你的Flutter环境已经正确配置。
- 在你的
pubspec.yaml
文件中添加geofence_foreground_service
依赖:
dependencies:
flutter:
sdk: flutter
geofence_foreground_service: ^最新版本号 # 请替换为最新版本号
配置Android权限
在android/app/src/main/AndroidManifest.xml
中添加必要的权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<application
...>
<!-- 添加服务声明 -->
<service
android:name=".GeofenceForegroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />
<!-- 其他配置 -->
</application>
</manifest>
初始化插件和配置前台服务
在你的Flutter项目中,初始化geofence_foreground_service
插件并配置前台服务。
import 'package:flutter/material.dart';
import 'package:geofence_foreground_service/geofence_foreground_service.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GeofenceForegroundService? _geofenceService;
@override
void initState() {
super.initState();
_initializeGeofenceService();
}
void _initializeGeofenceService() async {
// 初始化插件
_geofenceService = await GeofenceForegroundService.instance;
// 配置前台服务通知
Notification notification = Notification(
title: "Geofence Service",
text: "Running in the foreground",
icon: "mipmap/ic_launcher", // 替换为你的应用图标资源
smallIcon: "mipmap/ic_launcher_round", // 替换为你的应用小图标资源
importance: NotificationImportance.HIGH,
priority: NotificationPriority.HIGH,
channelId: "geofence_channel",
channelName: "Geofence Channel",
channelDescription: "Channel for geofence notifications",
);
// 启动前台服务
await _geofenceService!.startForegroundService(
notification: notification,
geofences: [
Geofence(
requestId: "geofence_1",
latitude: 37.7749,
longitude: -122.4194,
radius: 1000.0, // 半径1公里
transitionTypes: [
GeofenceTransition.ENTER,
GeofenceTransition.EXIT,
],
),
],
);
// 监听地理围栏事件
_geofenceService!.geofenceEvents.listen((event) {
print("Geofence event: ${event.requestId}, Transition: ${event.transitionType}");
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Geofence Foreground Service Example'),
),
body: Center(
child: Text('Check the console for geofence events.'),
),
),
);
}
}
处理权限请求
在实际应用中,你还需要处理权限请求,确保应用在运行时请求并获得了必要的权限。这里是一个简单的权限请求示例:
import 'package:permission_handler/permission_handler.dart';
Future<void> _requestPermissions() async {
Map<Permission, PermissionStatus> statuses = await Permission.requestMultiple([
Permission.location,
Permission.activityRecognition,
]);
// 处理权限请求结果
statuses.forEach((permission, status) {
if (status.isGranted) {
print("${permission.toString()} permission granted.");
} else if (status.isDenied) {
print("${permission.toString()} permission denied.");
} else if (status.isPermanentlyDenied) {
print("${permission.toString()} permission permanently denied.");
// 引导用户到设置页面手动开启权限
openAppSettings();
}
});
}
在initState
方法中调用_requestPermissions()
函数:
@override
void initState() {
super.initState();
_requestPermissions();
_initializeGeofenceService();
}
注意事项
- 请确保你已经添加了
permission_handler
插件到你的pubspec.yaml
中,用于处理权限请求。 - 前台服务通知的图标和资源需要放在Android项目的
res/mipmap
目录下。 - 这是一个基本的示例,实际项目中可能还需要处理更多的错误情况和边界情况。
希望这能帮助你在Flutter项目中成功集成并使用geofence_foreground_service
插件。