You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 13:33:15