Flutter for OpenHarmony:三方库引入geolocator——地理位置定位服务
在移动应用开发中,地理位置服务是许多应用的核心功能:场景一:地图导航应用需要实时获取用户位置场景二:外卖配送应用需要追踪骑手位置场景三:社交应用需要基于位置推荐附近的人场景四:天气应用需要根据位置显示当地天气场景五:运动健身应用需要记录运动轨迹 是 Flutter 中最流行的地理位置插件!它提供了跨平台的位置服务 API,支持获取当前位置、监听位置变化、检查权限等功能,在 OpenHarmony

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
🎯 前言:为什么需要地理位置服务?
在移动应用开发中,地理位置服务是许多应用的核心功能:
场景一:地图导航应用需要实时获取用户位置
场景二:外卖配送应用需要追踪骑手位置
场景三:社交应用需要基于位置推荐附近的人
场景四:天气应用需要根据位置显示当地天气
场景五:运动健身应用需要记录运动轨迹
geolocator 是 Flutter 中最流行的地理位置插件!它提供了跨平台的位置服务 API,支持获取当前位置、监听位置变化、检查权限等功能,在 OpenHarmony 平台上基于鸿蒙原生定位服务实现。
🚀 核心能力一览
| 功能特性 | 详细说明 | OpenHarmony 支持 |
|---|---|---|
| 获取当前位置 | 获取设备当前地理位置 | ✅ |
| 位置权限管理 | 检查和请求位置权限 | ✅ |
| 定位精度设置 | 设置定位精度级别 | ✅ |
| 最后已知位置 | 获取最后一次定位结果 | ✅ |
| 定位服务状态 | 检查定位服务是否开启 | ✅ |
| 打开设置页面 | 跳转到系统设置页面 | ✅ |
| 位置精度状态 | 获取位置精度状态 | ✅ |
| 后台定位 | 支持后台获取位置(需权限) | ✅ |
| 距离计算 | 计算两点之间的距离 | ✅ |
| 方位角计算 | 计算两点之间的方位角 | ✅ |
支持的功能
| 功能 | 说明 | OpenHarmony 支持 |
|---|---|---|
| getCurrentPosition | 获取当前位置 | ✅ |
| getLastKnownPosition | 获取最后已知位置 | ✅ |
| checkPermission | 检查位置权限 | ✅ |
| requestPermission | 请求位置权限 | ✅ |
| isLocationServiceEnabled | 检查定位服务状态 | ✅ |
| getLocationAccuracy | 获取位置精度状态 | ✅ |
| openAppSettings | 打开应用设置 | ✅ |
| openLocationSettings | 打开位置设置 | ✅ |
| distanceBetween | 计算两点距离 | ✅ |
| bearingBetween | 计算方位角 | ✅ |
⚙️ 环境准备
第一步:添加依赖
📄 pubspec.yaml:
dependencies:
flutter:
sdk: flutter
# 添加 geolocator 依赖(OpenHarmony 适配版本)
geolocator:
git:
url: https://atomgit.com/openharmony-sig/fluttertpc_geolocator.git
path: geolocator
执行命令:
flutter pub get
第二步:配置位置权限
geolocator 需要位置权限才能获取地理位置信息。
2.1 添加权限到 module.json5
📄 ohos/entry/src/main/module.json5:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:locationapprox",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "$string:locationbackground",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
}
]
}
}
2.2 添加权限说明
📄 ohos/entry/src/main/resources/base/element/string.json:
{
"string": [
{
"name": "location",
"value": "用于获取您的位置信息"
},
{
"name": "locationapprox",
"value": "用于获取您的大致位置"
},
{
"name": "locationbackground",
"value": "用于在后台获取您的位置"
}
]
}
第三步:权限说明
权限类型:
ohos.permission.LOCATION:精确位置权限(必需)ohos.permission.APPROXIMATELY_LOCATION:大致位置权限(可选)ohos.permission.LOCATION_IN_BACKGROUND:后台位置权限(可选,仅在需要后台定位时添加)
注意:部分权限为 system_basic 级别,默认应用权限为 normal。如果安装 HAP 时报错 9568289,需要修改应用权限级别。详见官方文档。
📸 场景一:获取当前位置

📝 完整代码
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '位置获取示例',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF2196F3)),
useMaterial3: true,
),
home: const CurrentLocationPage(),
);
}
}
class CurrentLocationPage extends StatefulWidget {
const CurrentLocationPage({super.key});
State<CurrentLocationPage> createState() => _CurrentLocationPageState();
}
class _CurrentLocationPageState extends State<CurrentLocationPage> {
Position? _currentPosition;
bool _isLoading = false;
String _statusMessage = '点击按钮获取位置';
void initState() {
super.initState();
_checkPermissions();
}
// 检查权限
Future<void> _checkPermissions() async {
final permission = await Geolocator.checkPermission();
setState(() {
_statusMessage = '当前权限状态: ${_getPermissionText(permission)}';
});
}
String _getPermissionText(LocationPermission permission) {
switch (permission) {
case LocationPermission.denied:
return '未授权';
case LocationPermission.deniedForever:
return '永久拒绝';
case LocationPermission.whileInUse:
return '使用时允许';
case LocationPermission.always:
return '始终允许';
default:
return '未知';
}
}
// 获取当前位置
Future<void> _getCurrentLocation() async {
setState(() {
_isLoading = true;
_statusMessage = '正在获取位置...';
});
try {
// 1. 检查定位服务是否开启
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
setState(() {
_statusMessage = '定位服务未开启';
_isLoading = false;
});
_showLocationServiceDialog();
return;
}
// 2. 检查权限
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
setState(() {
_statusMessage = '位置权限被拒绝';
_isLoading = false;
});
return;
}
}
if (permission == LocationPermission.deniedForever) {
setState(() {
_statusMessage = '位置权限被永久拒绝,请在设置中开启';
_isLoading = false;
});
return;
}
// 3. 获取当前位置
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
setState(() {
_currentPosition = position;
_statusMessage = '位置获取成功';
_isLoading = false;
});
} catch (e) {
setState(() {
_statusMessage = '获取位置失败: $e';
_isLoading = false;
});
}
}
// 显示定位服务对话框
void _showLocationServiceDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('定位服务未开启'),
content: const Text('请在设置中开启定位服务'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
Geolocator.openLocationSettings();
},
child: const Text('去设置'),
),
],
),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('获取当前位置'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 状态信息
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'状态',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(_statusMessage),
],
),
),
),
const SizedBox(height: 16),
// 位置信息
if (_currentPosition != null)
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'位置信息',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
_buildInfoRow('纬度', '${_currentPosition!.latitude}'),
_buildInfoRow('经度', '${_currentPosition!.longitude}'),
_buildInfoRow('海拔', '${_currentPosition!.altitude} 米'),
_buildInfoRow('精度', '${_currentPosition!.accuracy} 米'),
_buildInfoRow('速度', '${_currentPosition!.speed} m/s'),
_buildInfoRow('方向', '${_currentPosition!.heading}°'),
_buildInfoRow(
'时间',
_currentPosition!.timestamp?.toString() ?? '未知',
),
],
),
),
),
const Spacer(),
// 获取位置按钮
ElevatedButton.icon(
onPressed: _isLoading ? null : _getCurrentLocation,
icon: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.my_location),
label: Text(_isLoading ? '获取中...' : '获取当前位置'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
),
),
],
),
),
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
SizedBox(
width: 60,
child: Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(
child: Text(value),
),
],
),
);
}
}
🔑 关键点解析
- isLocationServiceEnabled:检查定位服务是否开启
- checkPermission:检查位置权限状态
- requestPermission:请求位置权限
- getCurrentPosition:获取当前位置,可设置精度和距离过滤
- LocationSettings:定位设置,包括精度级别和距离过滤器
- Position:位置信息对象,包含经纬度、海拔、精度等信息
🎨 场景二:距离计算与位置精度

📝 完整代码
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '距离计算示例',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF4CAF50)),
useMaterial3: true,
),
home: const DistanceCalculatorPage(),
);
}
}
class DistanceCalculatorPage extends StatefulWidget {
const DistanceCalculatorPage({super.key});
State<DistanceCalculatorPage> createState() => _DistanceCalculatorPageState();
}
class _DistanceCalculatorPageState extends State<DistanceCalculatorPage> {
Position? _currentPosition;
LocationAccuracyStatus _accuracyStatus = LocationAccuracyStatus.reduced;
// 预设的一些地点
final List<Map<String, dynamic>> _landmarks = [
{'name': '北京天安门', 'lat': 39.9042, 'lon': 116.4074},
{'name': '上海东方明珠', 'lat': 31.2397, 'lon': 121.4997},
{'name': '深圳市民中心', 'lat': 22.5455, 'lon': 114.0545},
{'name': '杭州西湖', 'lat': 30.2489, 'lon': 120.1511},
];
void initState() {
super.initState();
_initLocation();
}
Future<void> _initLocation() async {
await _getCurrentLocation();
await _getLocationAccuracy();
}
// 获取当前位置
Future<void> _getCurrentLocation() async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return;
}
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return;
}
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
setState(() {
_currentPosition = position;
});
} catch (e) {
print('获取位置失败: $e');
}
}
// 获取位置精度状态
Future<void> _getLocationAccuracy() async {
try {
final accuracy = await Geolocator.getLocationAccuracy();
setState(() {
_accuracyStatus = accuracy;
});
} catch (e) {
print('获取精度状态失败: $e');
}
}
// 计算距离
double _calculateDistance(double lat, double lon) {
if (_currentPosition == null) return 0;
return Geolocator.distanceBetween(
_currentPosition!.latitude,
_currentPosition!.longitude,
lat,
lon,
);
}
// 计算方位角
double _calculateBearing(double lat, double lon) {
if (_currentPosition == null) return 0;
return Geolocator.bearingBetween(
_currentPosition!.latitude,
_currentPosition!.longitude,
lat,
lon,
);
}
// 格式化距离
String _formatDistance(double meters) {
if (meters < 1000) {
return '${meters.toStringAsFixed(0)} 米';
} else {
return '${(meters / 1000).toStringAsFixed(2)} 公里';
}
}
// 获取方向文字
String _getDirectionText(double bearing) {
if (bearing >= 337.5 || bearing < 22.5) return '正北';
if (bearing >= 22.5 && bearing < 67.5) return '东北';
if (bearing >= 67.5 && bearing < 112.5) return '正东';
if (bearing >= 112.5 && bearing < 157.5) return '东南';
if (bearing >= 157.5 && bearing < 202.5) return '正南';
if (bearing >= 202.5 && bearing < 247.5) return '西南';
if (bearing >= 247.5 && bearing < 292.5) return '正西';
return '西北';
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('距离计算'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _initLocation,
),
],
),
body: _currentPosition == null
? const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('正在获取位置...'),
],
),
)
: ListView(
padding: const EdgeInsets.all(16),
children: [
// 当前位置信息
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'当前位置',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text('纬度: ${_currentPosition!.latitude.toStringAsFixed(6)}'),
Text('经度: ${_currentPosition!.longitude.toStringAsFixed(6)}'),
Text('精度: ${_currentPosition!.accuracy.toStringAsFixed(2)} 米'),
const SizedBox(height: 8),
Row(
children: [
const Text('位置精度状态: '),
Chip(
label: Text(
_accuracyStatus == LocationAccuracyStatus.precise
? '精确'
: '大致',
),
backgroundColor: _accuracyStatus == LocationAccuracyStatus.precise
? Colors.green[100]
: Colors.orange[100],
),
],
),
],
),
),
),
const SizedBox(height: 16),
// 地标列表
Text(
'到各地标的距离',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
..._landmarks.map((landmark) {
final distance = _calculateDistance(
landmark['lat'],
landmark['lon'],
);
final bearing = _calculateBearing(
landmark['lat'],
landmark['lon'],
);
final direction = _getDirectionText(bearing);
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: CircleAvatar(
child: Text(landmark['name'][0]),
),
title: Text(landmark['name']),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('距离: ${_formatDistance(distance)}'),
Text('方向: $direction (${bearing.toStringAsFixed(1)}°)'),
],
),
trailing: Icon(
Icons.navigation,
color: Colors.blue,
size: 32,
),
),
);
}).toList(),
],
),
);
}
}
🔑 关键点解析
- distanceBetween:计算两个坐标点之间的距离(米)
- bearingBetween:计算从起点到终点的方位角(度)
- getLocationAccuracy:获取位置精度状态(精确或大致)
- LocationAccuracyStatus:位置精度状态枚举
- 距离格式化:将米转换为公里显示
- 方向判断:根据方位角判断方向(东南西北)
📊 场景三:最后已知位置与设置跳转
📝 完整代码
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '位置管理示例',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFFFF9800)),
useMaterial3: true,
),
home: const LocationManagementPage(),
);
}
}
class LocationManagementPage extends StatefulWidget {
const LocationManagementPage({super.key});
State<LocationManagementPage> createState() => _LocationManagementPageState();
}
class _LocationManagementPageState extends State<LocationManagementPage> {
Position? _lastKnownPosition;
Position? _currentPosition;
bool _isServiceEnabled = false;
LocationPermission _permission = LocationPermission.denied;
LocationAccuracyStatus _accuracyStatus = LocationAccuracyStatus.reduced;
void initState() {
super.initState();
_checkStatus();
}
// 检查所有状态
Future<void> _checkStatus() async {
await _checkLocationService();
await _checkPermission();
await _getLocationAccuracy();
await _getLastKnownPosition();
}
// 检查定位服务
Future<void> _checkLocationService() async {
try {
final enabled = await Geolocator.isLocationServiceEnabled();
setState(() {
_isServiceEnabled = enabled;
});
} catch (e) {
print('检查定位服务失败: $e');
}
}
// 检查权限
Future<void> _checkPermission() async {
try {
final permission = await Geolocator.checkPermission();
setState(() {
_permission = permission;
});
} catch (e) {
print('检查权限失败: $e');
}
}
// 请求权限
Future<void> _requestPermission() async {
try {
final permission = await Geolocator.requestPermission();
setState(() {
_permission = permission;
});
if (permission == LocationPermission.whileInUse ||
permission == LocationPermission.always) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('权限已授予')),
);
}
} catch (e) {
print('请求权限失败: $e');
}
}
// 获取位置精度状态
Future<void> _getLocationAccuracy() async {
try {
final accuracy = await Geolocator.getLocationAccuracy();
setState(() {
_accuracyStatus = accuracy;
});
} catch (e) {
print('获取精度状态失败: $e');
}
}
// 获取最后已知位置
Future<void> _getLastKnownPosition() async {
try {
final position = await Geolocator.getLastKnownPosition();
setState(() {
_lastKnownPosition = position;
});
} catch (e) {
print('获取最后已知位置失败: $e');
}
}
// 获取当前位置
Future<void> _getCurrentPosition() async {
try {
if (!_isServiceEnabled) {
_showServiceDialog();
return;
}
if (_permission == LocationPermission.denied ||
_permission == LocationPermission.deniedForever) {
_showPermissionDialog();
return;
}
final position = await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
),
);
setState(() {
_currentPosition = position;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('位置获取成功')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('获取位置失败: $e')),
);
}
}
// 显示定位服务对话框
void _showServiceDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('定位服务未开启'),
content: const Text('请在设置中开启定位服务'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
Geolocator.openLocationSettings();
},
child: const Text('去设置'),
),
],
),
);
}
// 显示权限对话框
void _showPermissionDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('需要位置权限'),
content: const Text('请授予位置权限以使用此功能'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
if (_permission == LocationPermission.deniedForever) {
Geolocator.openAppSettings();
} else {
_requestPermission();
}
},
child: Text(
_permission == LocationPermission.deniedForever
? '去设置'
: '授权',
),
),
],
),
);
}
String _getPermissionText(LocationPermission permission) {
switch (permission) {
case LocationPermission.denied:
return '未授权';
case LocationPermission.deniedForever:
return '永久拒绝';
case LocationPermission.whileInUse:
return '使用时允许';
case LocationPermission.always:
return '始终允许';
default:
return '未知';
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('位置管理'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _checkStatus,
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// 状态卡片
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'系统状态',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
_buildStatusRow(
'定位服务',
_isServiceEnabled ? '已开启' : '未开启',
_isServiceEnabled ? Colors.green : Colors.red,
),
_buildStatusRow(
'位置权限',
_getPermissionText(_permission),
_permission == LocationPermission.whileInUse ||
_permission == LocationPermission.always
? Colors.green
: Colors.orange,
),
_buildStatusRow(
'位置精度',
_accuracyStatus == LocationAccuracyStatus.precise
? '精确'
: '大致',
_accuracyStatus == LocationAccuracyStatus.precise
? Colors.green
: Colors.orange,
),
],
),
),
),
const SizedBox(height: 16),
// 最后已知位置
if (_lastKnownPosition != null)
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'最后已知位置',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text('纬度: ${_lastKnownPosition!.latitude.toStringAsFixed(6)}'),
Text('经度: ${_lastKnownPosition!.longitude.toStringAsFixed(6)}'),
Text('精度: ${_lastKnownPosition!.accuracy.toStringAsFixed(2)} 米'),
if (_lastKnownPosition!.timestamp != null)
Text('时间: ${_lastKnownPosition!.timestamp}'),
],
),
),
),
const SizedBox(height: 16),
// 当前位置
if (_currentPosition != null)
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'当前位置',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text('纬度: ${_currentPosition!.latitude.toStringAsFixed(6)}'),
Text('经度: ${_currentPosition!.longitude.toStringAsFixed(6)}'),
Text('精度: ${_currentPosition!.accuracy.toStringAsFixed(2)} 米'),
Text('海拔: ${_currentPosition!.altitude.toStringAsFixed(2)} 米'),
Text('速度: ${_currentPosition!.speed.toStringAsFixed(2)} m/s'),
Text('方向: ${_currentPosition!.heading.toStringAsFixed(2)}°'),
],
),
),
),
const SizedBox(height: 16),
// 操作按钮
ElevatedButton.icon(
onPressed: _getCurrentPosition,
icon: const Icon(Icons.my_location),
label: const Text('获取当前位置'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _requestPermission,
icon: const Icon(Icons.security),
label: const Text('请求位置权限'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () => Geolocator.openLocationSettings(),
icon: const Icon(Icons.settings),
label: const Text('打开位置设置'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () => Geolocator.openAppSettings(),
icon: const Icon(Icons.app_settings_alt),
label: const Text('打开应用设置'),
),
],
),
);
}
Widget _buildStatusRow(String label, String value, Color color) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: color.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: Text(
value,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}
}
🔑 关键点解析
- getLastKnownPosition:获取最后一次定位结果,速度快但可能不是最新位置
- openLocationSettings:打开系统位置设置页面
- openAppSettings:打开应用设置页面
- 状态管理:统一管理定位服务、权限、精度等状态
- 用户引导:通过对话框引导用户开启服务或授予权限
📚 API 参考
Geolocator 静态方法
| 方法 | 返回值 | 说明 | OpenHarmony 支持 |
|---|---|---|---|
| getCurrentPosition({desiredAccuracy, forceAndroidLocationManager, timeLimit}) | Future | 获取当前位置 | ✅ |
| getLastKnownPosition() | Future<Position?> | 获取最后已知位置 | ✅ |
| checkPermission() | Future | 检查位置权限 | ✅ |
| requestPermission() | Future | 请求位置权限 | ✅ |
| isLocationServiceEnabled() | Future | 检查定位服务是否开启 | ✅ |
| getLocationAccuracy() | Future | 获取位置精度状态 | ✅ |
| openAppSettings() | Future | 打开应用设置 | ✅ |
| openLocationSettings() | Future | 打开位置设置 | ✅ |
| distanceBetween(lat1, lon1, lat2, lon2) | double | 计算两点距离(米) | ✅ |
| bearingBetween(lat1, lon1, lat2, lon2) | double | 计算方位角(度) | ✅ |
getCurrentPosition 参数
| 参数 | 类型 | 说明 | 默认值 | OpenHarmony 支持 |
|---|---|---|---|---|
| desiredAccuracy | LocationAccuracy | 定位精度级别 | LocationAccuracy.best | ✅ |
| forceAndroidLocationManager | bool | 强制使用 Android 位置管理器 | false | ❌ |
| timeLimit | Duration? | 超时时间 | null | ❌ |
LocationAccuracy 枚举
| 值 | 说明 | 精度范围 | OpenHarmony 支持 |
|---|---|---|---|
| lowest | 最低精度 | ~3000m (iOS), ~500m (Android) | ✅ |
| low | 低精度 | ~1000m (iOS), ~500m (Android) | ✅ |
| medium | 中等精度 | ~100m | ✅ |
| high | 高精度 | ~0-100m | ✅ |
| best | 最佳精度 | 最高精度 | ✅ |
| bestForNavigation | 导航最佳精度 | 导航优化 | ✅ |
| reduced | 降低精度(iOS 14+) | 降低精度 | ❌ |
LocationPermission 枚举
| 值 | 说明 | OpenHarmony 支持 |
|---|---|---|
| denied | 权限被拒绝 | ✅ |
| deniedForever | 权限被永久拒绝 | ✅ |
| whileInUse | 使用时允许 | ✅ |
| always | 始终允许 | ✅ |
| unableToDetermine | 无法确定(仅 Web) | ❌ |
LocationAccuracyStatus 枚举
| 值 | 说明 | OpenHarmony 支持 |
|---|---|---|
| reduced | 大致位置 | ✅ |
| precise | 精确位置 | ✅ |
| unknown | 未知(仅 Android) | ❌ |
Position 对象属性
| 属性 | 类型 | 说明 | OpenHarmony 支持 |
|---|---|---|---|
| latitude | double | 纬度(-90 到 +90) | ✅ |
| longitude | double | 经度(-180 到 +180) | ✅ |
| timestamp | DateTime? | 定位时间 | ✅ |
| altitude | double | 海拔高度(米) | ✅ |
| altitudeAccuracy | double | 海拔精度(米) | ✅ |
| accuracy | double | 水平精度(米) | ✅ |
| heading | double | 方向角(度,0-360) | ✅ |
| headingAccuracy | double | 方向精度(度) | ❌ |
| floor | int? | 楼层 | ❌ |
| speed | double | 速度(m/s) | ✅ |
| speedAccuracy | double | 速度精度(m/s) | ✅ |
| isMocked | bool | 是否为模拟位置 | ✅ |
💡 最佳实践
1. 完整的权限检查流程
Future<Position?> getLocationWithPermissionCheck() async {
// 1. 检查定位服务
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// 引导用户开启定位服务
await Geolocator.openLocationSettings();
return null;
}
// 2. 检查权限
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// 权限被拒绝
return null;
}
}
if (permission == LocationPermission.deniedForever) {
// 权限被永久拒绝,引导用户到设置页面
await Geolocator.openAppSettings();
return null;
}
// 3. 获取位置
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
}
2. 优化定位性能
// 使用最后已知位置快速显示
Future<void> showLocationQuickly() async {
// 先显示最后已知位置
final lastKnown = await Geolocator.getLastKnownPosition();
if (lastKnown != null) {
updateUI(lastKnown);
}
// 然后获取最新位置
final current = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
updateUI(current);
}
// 根据场景选择合适的精度
LocationAccuracy getAccuracyForScenario(String scenario) {
switch (scenario) {
case 'navigation':
return LocationAccuracy.bestForNavigation;
case 'tracking':
return LocationAccuracy.high;
case 'general':
return LocationAccuracy.medium;
default:
return LocationAccuracy.low;
}
}
// 使用示例
Future<Position> getLocationForScenario(String scenario) async {
return await Geolocator.getCurrentPosition(
desiredAccuracy: getAccuracyForScenario(scenario),
);
}
3. 错误处理
Future<Position?> getLocationSafely() async {
try {
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
);
} on LocationServiceDisabledException {
// 定位服务未开启
print('定位服务未开启');
return null;
} on PermissionDeniedException {
// 权限被拒绝
print('位置权限被拒绝');
return null;
} catch (e) {
// 其他错误
print('获取位置失败: $e');
return null;
}
}
4. 距离和方向计算
// 计算两点之间的距离和方向
Map<String, dynamic> calculateDistanceAndBearing(
double lat1,
double lon1,
double lat2,
double lon2,
) {
final distance = Geolocator.distanceBetween(lat1, lon1, lat2, lon2);
final bearing = Geolocator.bearingBetween(lat1, lon1, lat2, lon2);
return {
'distance': distance,
'distanceKm': distance / 1000,
'bearing': bearing,
'direction': getDirectionFromBearing(bearing),
};
}
String getDirectionFromBearing(double bearing) {
const directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
final index = ((bearing + 22.5) / 45).floor() % 8;
return directions[index];
}
⚠️ 常见问题
问题1:无法获取位置
错误信息:
LocationServiceDisabledException: Location services are disabled
解决方案:
- 检查设备定位服务是否开启
- 使用
isLocationServiceEnabled()检查状态 - 引导用户使用
openLocationSettings()开启定位服务
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
await Geolocator.openLocationSettings();
}
问题2:权限请求失败
错误信息:
PermissionDeniedException: User denied permissions to access the device's location
解决方案:
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.deniedForever) {
// 引导用户到设置页面
await Geolocator.openAppSettings();
}
问题3:安装HAP时报错 9568289
错误信息:
error: install failed due to grant request permissions failed
原因:部分权限为 system_basic 级别,默认应用权限为 normal。
解决方案:
修改应用权限级别为 system_basic。详见官方文档。
问题4:定位精度不准确
解决方案:
// 1. 使用高精度模式
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best,
);
// 2. 检查位置精度状态
final accuracyStatus = await Geolocator.getLocationAccuracy();
if (accuracyStatus == LocationAccuracyStatus.reduced) {
// 提示用户当前为大致位置
}
// 3. 检查返回的精度值
if (position.accuracy > 50) {
// 精度较低,可能需要重新定位
}
问题5:后台定位不工作
解决方案:
- 确保添加了后台位置权限:
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "$string:locationbackground",
"usedScene": {
"abilities": ["FormAbility"],
"when": "inuse"
}
}
- 确保添加了后台运行权限:
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
}
问题6:getLastKnownPosition 返回 null
原因:设备从未进行过定位,或者缓存已被清除。
解决方案:
// 先尝试获取最后已知位置
Position? lastKnown = await Geolocator.getLastKnownPosition();
if (lastKnown == null) {
// 如果没有,则获取当前位置
lastKnown = await Geolocator.getCurrentPosition();
}
问题7:距离计算不准确
解决方案:
// distanceBetween 使用 Haversine 公式计算球面距离
// 对于短距离(< 1km)精度较高
// 对于长距离可能有一定误差
double distance = Geolocator.distanceBetween(
lat1, lon1, lat2, lon2,
);
// 如果需要更高精度,可以考虑使用其他地理计算库
🎯 总结
通过本文,你已经掌握了:
✅ geolocator 插件的基本使用方法
✅ 完整的权限检查和请求流程
✅ 获取当前位置和最后已知位置
✅ 定位精度设置和状态检查
✅ 距离和方位角计算
✅ 定位服务和应用设置跳转
✅ 最佳实践和性能优化
✅ 常见问题解决方案
geolocator 为 Flutter 应用提供了强大的地理位置服务能力,在 OpenHarmony 平台上基于鸿蒙原生定位服务实现,性能稳定可靠。开始在你的应用中使用 geolocator,打造基于位置的精彩功能吧!
📖 参考资源:
更多推荐
所有评论(0)