Flutter多地理围栏(Geofencing)触发识别实现方案咨询
无需原生代码的Flutter多地理围栏实现方案
推荐使用 geofence_service 包
这个Flutter包原生支持Android和iOS平台,无需编写原生代码,能直接识别触发事件的具体围栏,完全匹配你的需求。
1. 添加依赖
在项目的 pubspec.yaml 中加入以下依赖(请替换为最新版本号):
dependencies: geofence_service: ^最新版本号 permission_handler: ^最新版本号 # 用于申请位置权限
2. 配置平台权限
- Android:在
android/app/src/main/AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- iOS:在
ios/Runner/Info.plist中添加权限描述:
<key>NSLocationWhenInUseUsageDescription</key> <string>需要访问您的位置以追踪地理围栏事件</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>需要在后台访问您的位置以追踪地理围栏事件</string>
3. 核心实现代码
以下是完整的示例代码,包含围栏添加、事件监听和触发围栏的识别逻辑:
import 'package:flutter/material.dart'; import 'package:geofence_service/geofence_service.dart'; import 'package:permission_handler/permission_handler.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: GeofenceDemoPage(), ); } } class GeofenceDemoPage extends StatefulWidget { @override State<GeofenceDemoPage> createState() => _GeofenceDemoPageState(); } class _GeofenceDemoPageState extends State<GeofenceDemoPage> { // 初始化地理围栏服务 final _geofenceService = GeofenceService.instance.setup( interval: 5000, // 位置更新间隔(毫秒) accuracy: LocationAccuracy.high, loiteringDelayMs: 60000, // 逗留状态判定延迟 statusChangeDelayMs: 10000, // 状态变更防抖延迟 ); // 定义多个地理围栏,每个围栏用唯一ID标识 final _geofences = <Geofence>[ Geofence( id: 'home_fence', latitude: 37.7749, longitude: -122.4194, radius: [GeofenceRadius(id: '100m', length: 100)], ), Geofence( id: 'office_fence', latitude: 34.0522, longitude: -118.2437, radius: [GeofenceRadius(id: '200m', length: 200)], ), ]; late StreamSubscription<GeofenceStatus> _statusSubscription; @override void initState() { super.initState(); _requestLocationPermission(); _setupGeofenceListeners(); _geofenceService.addGeofences(_geofences); } @override void dispose() { _statusSubscription.cancel(); _geofenceService.stop(); super.dispose(); } // 请求位置权限 Future<void> _requestLocationPermission() async { final status = await Permission.locationAlways.request(); if (status != PermissionStatus.granted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('未授予位置权限,无法使用地理围栏功能')), ); } } // 设置围栏事件监听 void _setupGeofenceListeners() { _statusSubscription = _geofenceService.geofenceStatusStream.listen((status) { // 通过 status.geofence.id 精准识别触发的围栏 final fenceId = status.geofence.id; final fenceStatus = status.status; print('触发围栏:$fenceId,状态:${fenceStatus.name}'); // 这里可以根据围栏ID和状态执行业务逻辑,比如发送通知、更新UI等 }); // 启动地理围栏服务 _geofenceService.start(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('地理围栏测试')), body: const Center( child: Text('地理围栏服务已启动,查看控制台输出'), ), ); } }
关键说明
- 每个
Geofence对象的id属性是唯一标识,触发事件时通过status.geofence.id即可确定是哪个围栏触发了事件 - 支持为单个围栏设置多个半径,满足不同范围的监测需求
- 必须申请
locationAlways权限,才能在后台持续追踪围栏事件 - 可通过
GeofenceStatus的status属性判断事件类型:ENTER(进入)、EXIT(退出)、DWELL(逗留)
替代方案:自定义地理围栏逻辑
如果不想用第三方围栏包,也可以用 geolocator 包定期获取位置,自行计算与围栏的距离来判断状态:
import 'package:geolocator/geolocator.dart'; // 自定义围栏模型 class CustomGeofence { final String id; final double lat; final double lng; final double radius; String? currentStatus; CustomGeofence({required this.id, required this.lat, required this.lng, required this.radius}); } // 定义围栏列表 final myFences = [ CustomGeofence(id: 'home', lat: 37.7749, lng: -122.4194, radius: 100), ]; // 检查当前位置与所有围栏的关系 void checkGeofenceStatus(Position position) { for (var fence in myFences) { final distance = Geolocator.distanceBetween( position.latitude, position.longitude, fence.lat, fence.lng, ); if (distance <= fence.radius) { if (fence.currentStatus != 'enter') { print('进入围栏:${fence.id}'); fence.currentStatus = 'enter'; // 执行进入逻辑 } } else { if (fence.currentStatus != 'exit') { print('退出围栏:${fence.id}'); fence.currentStatus = 'exit'; // 执行退出逻辑 } } } } // 启动位置追踪 void startTracking() { Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 10, // 移动10米才更新位置 ), ).listen(checkGeofenceStatus); }
这种方式需要自行处理状态切换的防抖逻辑,后台运行的配置也更繁琐,适合对逻辑有高度自定义需求的场景。
内容的提问来源于stack exchange,提问作者gariva
相关产品推荐
相关产品推荐

