本文使用geolocator插件实现app物理定位功能。
该插件的主要功能有:
- 获取最后已知位置;
- 获取设备当前位置;
- 获取连续的位置更新;
- 检查设备是否启用了定位服务;
- 计算两个地理坐标之间的距离(米);
- 计算两个地理坐标之间的方位;
如何安装:
方法一:在pubspec.yaml文件中添加它,10.1.0是当前最新版本号,也可指定特定版本号。
然后在终端运行以下命令:
flutter packages get
方法二:直接在终端运行以下命令获取最新版本geolocator插件
flutter pub add geolocator
配置app环境 :
Android系统
1.在 "android\gradle.properties" 文件中添加以下内容:
android.useAndroidX=true
android.enableJetifier=true
2.确保将 "android/app/build.gradle "文件中的 compileSdkVersion 设置为 33(10.1.0版本时要求,不同版本可能要求不一样,具体看原依赖文档):
android {compileSdkVersion 33...
}
3.确保将所有 android. 依赖项替换为 AndroidX 对应项(完整列表可在此处找到:迁移到 AndroidX)。
4.打开 "android\app\src\main\AndroidManifest.xml" 文件,将以下两行中的一行添加为<manifest> 标签的直接子代(当这两种权限都配置时,geolocator将使用 ACCESS_FINE_LOCATION):
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
如果你目标用户用的是Android10或者以上的系统,就需要再添加ACCESS_BACKGROUND_LOCATION这条权限
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
IOS系统
打开 "ios\Runner\Info.plist" 文件,添加以下内容。string标签内为描述,可更改以贴合上下文。
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
如果你想在应用程序处于后台时也能接收位置更新,你还需要在 XCode 项目中添加后台模式功能(项目 > 签名和功能 > "+ 功能 "按钮)并选择位置更新。(但要注意,在向 AppStore 提交应用程序时,你需要向苹果公司详细解释为什么您的应用程序需要这样做。如果苹果不满意您的解释,您的应用程序将被拒绝。)
使用 requestTemporaryFullAccuracy({purposeKey: "YourPurposeKey"}) 方法时,应在 Info.plist 文件中添加一个字典。第二个键(本例中称为 YourPurposeKey)应与 requestTemporaryFullAccuracy() 方法中传递的 purposeKey 相匹配
<key>NSLocationTemporaryUsageDescriptionDictionary</key> <dict><key>YourPurposeKey</key><string>The example App requires temporary access to the device's precise location.</string> </dict>
简单使用举例:
权限配置完成后我们就可以开始使用地理定位依赖了,下面的代码举例说明了如何获取设备的当前位置,包括检查定位服务是否启用,以及检查/请求访问设备位置的权限:
/// 位置服务Future _determinePosition() async {bool serviceEnabled;LocationPermission permission;double longitude=0;double latitude=0;try {/// 手机GPS服务是否已启用。serviceEnabled = await Geolocator.isLocationServiceEnabled();if (!serviceEnabled) {//定位服务未启用,要求用户启用定位服务var res = await Geolocator.openLocationSettings();if (!res) {/// 被拒绝return;}}/// 是否允许app访问地理位置permission = await Geolocator.checkPermission();if (permission == LocationPermission.denied) {/// 之前访问设备位置的权限被拒绝,重新申请权限permission = await Geolocator.requestPermission();if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {/// 再次被拒绝。根据Android指南,你的应用现在应该显示一个解释性UI。return;}} else if (permission == LocationPermission.deniedForever) {/// 之前权限被永久拒绝,打开app权限设置页面await Geolocator.openAppSettings();return;}/// 允许访问地理位置,获取地理位置Position position = await Geolocator.getCurrentPosition();longitude = position.longitude;latitude = position.latitude;} catch (e) {print(e);}}
geolocator依赖的完整API:
检查GPS是否启用:isLocationServiceEnabled
可用该方法检查手机GPS(全球定位系统)是否打开,以此进行不同操作
import 'package:geolocator/geolocator.dart';bool isLocationServiceEnabled = await Geolocator.isLocationServiceEnabled();
然后可以调用 getServiceStatusStream监听服务状态变化。这将返回一个可以监听的 Stream<ServiceStatus>,以接收位置服务状态更新。
import 'package:geolocator/geolocator.dart';StreamSubscription<ServiceStatus> serviceStatusStream = Geolocator.getServiceStatusStream().listen((ServiceStatus status) {print(status);});
检查是否允许APP获取位置权限:checkPermission
import 'package:geolocator/geolocator.dart';LocationPermission permission = await Geolocator.checkPermission();
APP请求获取位置权限:requestPermission
import 'package:geolocator/geolocator.dart';LocationPermission permission = await Geolocator.requestPermission();
checkPermission 和 requestPermission 方法返回值说明
- denied:用户拒绝了访问设备位置的权限。您可以再次申请权限(这也是初始权限状态)。
- deniedForever:永久拒绝访问设备位置的权限。请求权限时,权限对话框将不会显示,直到用户在应用程序设置中更新权限。
- whileInUse:仅当应用程序正在使用时才允许访问设备位置。
- always:即使应用程序在后台运行,也允许访问设备位置。
打开相关设置页面:
在某些情况下,有必要询问用户并更新其设备设置。例如,用户最初永久拒绝了APP访问设备位置的权限,或者位置服务未启用(在 Android 上,自动解析不起作用)。在这种情况下,你可以使用 openAppSettings 或 openLocationSettings 方法立即将用户重定向到设备的设置页面。
在安卓系统中,openAppSettings 方法将把用户重定向到应用程序特定设置,用户可以在此重新设置APP相关权限。
openLocationSettings 方法将弹窗询问或把用户重定向到设备位置设置,用户可在此启用/禁用位置服务。
在 iOS 系统中,我们不允许打开特定的设置页面,因此这两种方法都会将用户重定向到 "设置应用",用户可以从该应用导航到正确的设置类别,以更新权限或启用/禁用位置服务。
import 'package:geolocator/geolocator.dart';await Geolocator.openAppSettings();
await Geolocator.openLocationSettings();
获取当前位置:getCurrentPosition
- desiredAccuracy:app希望接收的位置数据的精度(可选值在文章结尾有说明);
- timeLimit:获取当前位置所允许的最长时间。超过时间限制后,将抛出 TimeOutException 异常,并取消调用。默认情况下未配置任何限制,即永不超时。
import 'package:geolocator/geolocator.dart';Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
获取最后已知位置:getLastKnownPosition
import 'package:geolocator/geolocator.dart';Position? position = await Geolocator.getLastKnownPosition();
监听持续性的位置变化:getPositionStream
- accuracy:应用程序希望接收的位置数据的精确度;
- distanceFilter:在生成更新事件之前,设备水平移动的最小距离(以米为单位);
- timeLimit:位置更新之间允许的最长间隔时间。超过时限后,将抛出 TimeOutException 异常,并取消数据流。默认情况下未配置任何限制,即永不超时。
import 'package:geolocator/geolocator.dart';final LocationSettings locationSettings = LocationSettings(accuracy: LocationAccuracy.high,distanceFilter: 100,
);
StreamSubscription<Position> positionStream = Geolocator.getPositionStream(locationSettings: locationSettings).listen((Position? position) {print(position == null ? 'Unknown' : '${position.latitude.toString()}, ${position.longitude.toString()}');});
在某些情况下,有必要指定一些平台特定的设置。这可以通过使用特定平台的 AndroidSettings 或 AppleSettings 类来实现。使用特定平台类时,还必须导入特定平台包。示例:
import 'package:geolocator/geolocator.dart';
import 'package:geolocator_apple/geolocator_apple.dart';
import 'package:geolocator_android/geolocator_android.dart';late LocationSettings locationSettings;if (defaultTargetPlatform == TargetPlatform.android) {locationSettings = AndroidSettings(accuracy: LocationAccuracy.high,distanceFilter: 100,forceLocationManager: true,intervalDuration: const Duration(seconds: 10),//(可选)设置前台通知配置,使应用程序在进入后台时保持活跃foregroundNotificationConfig: const ForegroundNotificationConfig(notificationText:"Example app will continue to receive your location even when you aren't using it",notificationTitle: "Running in Background",enableWakeLock: true,));
} else if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) {locationSettings = AppleSettings(accuracy: LocationAccuracy.high,activityType: ActivityType.fitness,distanceFilter: 100,pauseLocationUpdatesAutomatically: true,// 只有当我们的APP在后台启动时才设置为trueshowBackgroundLocationIndicator: false,);
} else {locationSettings = LocationSettings(accuracy: LocationAccuracy.high,distanceFilter: 100,);
}StreamSubscription<Position> positionStream = Geolocator.getPositionStream(locationSettings: locationSettings).listen((Position? position) {print(position == null ? 'Unknown' : '${position.latitude.toString()}, ${position.longitude.toString()}');});
计算两个坐标间的距离:distanceBetween
- startLatitude:起始位置的经度。
- startLongitude:起始位置的纬度。
- endLatitude:终点位置的经度。
- endLongitude:终点位置的维度。
import 'package:geolocator/geolocator.dart';double distanceInMeters = Geolocator.distanceBetween(52.2165157, 6.9437819, 52.3546274, 4.8285838);
计算两个坐标间的方位:bearingBetween
同样需要上述四个参数。
import 'package:geolocator/geolocator.dart';double bearing = Geolocator.bearingBetween(52.2165157, 6.9437819, 52.3546274, 4.8285838);
定位精度枚举值
Android
- lowest:确保不会使用额外的功率来计算位置。这意味着该请求将充当被动监听器,只接收代表其他客户端计算的 "空闲 "位置,而不会只代表该请求计算位置。(可能不会返回任何位置信息)
- low:以可能牺牲定位精度为代价,对低功耗进行权衡。
- medium:要求在定位精度和耗电量之间进行权衡。
- high:要求权衡高精度定位和可能的额外耗电量。
IOS 请求的数据越详细,对电池消耗的影响就越大。
- lowest:精确到最近的三公里。
- low:精确到最近的一公里。
- medium:精度精确到一百米以内。
- high:精确到距离所需目标十米以内。
- best:可用的最佳精度级别。
- bestForNavigation:使用附加传感器数据以方便导航应用程序的最高精度。
好了,去玩吧