Flutter for OpenHarmony三方库适配实战:geocoding 地理编码
地理编码是将地址描述转换为地理坐标(经纬度),或将地理坐标转换为地址描述的过程。在移动应用开发中,地理编码广泛应用于地址搜索、位置标注、导航规划等场景。在 Flutter for OpenHarmony 应用开发中,geocoding是一个功能完善的地理编码插件,提供了完整的跨平台地理编码能力。geocoding 库为 Flutter for OpenHarmony 开发提供了完整的地理编码能力。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
本文基于flutter3.27.5开发

一、geocoding 库概述
地理编码是将地址描述转换为地理坐标(经纬度),或将地理坐标转换为地址描述的过程。在移动应用开发中,地理编码广泛应用于地址搜索、位置标注、导航规划等场景。在 Flutter for OpenHarmony 应用开发中,geocoding 是一个功能完善的地理编码插件,提供了完整的跨平台地理编码能力。
geocoding 库特点
geocoding 库基于 Flutter 平台接口实现,提供了以下核心特性:
正向地理编码:将文本地址转换为经纬度坐标,支持模糊搜索和精确匹配。
逆向地理编码:将经纬度坐标转换为详细的地址信息,包括国家、省份、城市、街道等。
多语言支持:支持设置语言环境,返回指定语言的地址信息。
服务状态检测:提供地理编码服务可用性检测,确保服务可靠性。
批量查询:支持返回多个匹配结果,提高地址解析成功率。
详细地址信息:提供完整的地址标记信息,包括行政区划、邮政编码等。
功能支持对比
| 功能 | Android | iOS | OpenHarmony |
|---|---|---|---|
| 地址转坐标 | ✅ | ✅ | ✅ |
| 坐标转地址 | ✅ | ✅ | ✅ |
| 多语言支持 | ✅ | ✅ | ✅ |
| 服务状态检测 | ✅ | ✅ | ✅ |
| 批量结果返回 | ✅ | ✅ | ✅ |
| 详细地址信息 | ✅ | ✅ | ✅ |
使用场景:地址搜索、位置标注、地图导航、外卖配送、打车应用、位置打卡等。
二、安装与配置
2.1 添加依赖
在项目的 pubspec.yaml 文件中添加 geocoding_ohos 依赖:
dependencies:
geocoding_ohos:
git:
url: https://atomgit.com/openharmony-sig/fluttertpc_geocoding.git
path: geocoding_ohos
然后执行以下命令获取依赖:
flutter pub get
2.2 权限配置
OpenHarmony 需要在配置文件中声明位置权限(地理编码服务需要位置权限)。在 ohos/entry/src/main/module.json5 中添加:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
在 ohos/entry/src/main/resources/base/element/string.json 中添加权限说明:
{
"string": [
{
"name": "location_reason",
"value": "应用需要位置权限来提供地理编码服务"
}
]
}
三、核心 API 详解
3.1 locationFromAddress 方法
locationFromAddress 方法用于将地址转换为经纬度坐标。
Future<List<Location>> locationFromAddress(String address)
参数说明:
address 参数是文本地址,支持模糊匹配和精确地址。
返回类型:List<Location> - 位置信息列表,可能包含多个匹配结果
使用示例:
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区中关村大街');
if (locations.isNotEmpty) {
Location location = locations.first;
print('纬度: ${location.latitude}');
print('经度: ${location.longitude}');
}
3.2 placemarkFromCoordinates 方法
placemarkFromCoordinates 方法用于将经纬度坐标转换为地址信息。
Future<List<Placemark>> placemarkFromCoordinates(double latitude, double longitude)
参数说明:
latitude 参数是纬度坐标(-90 到 90)。
longitude 参数是经度坐标(-180 到 180)。
返回类型:List<Placemark> - 地址标记列表,可能包含多个匹配结果
使用示例:
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(39.9042, 116.4074);
if (placemarks.isNotEmpty) {
Placemark placemark = placemarks.first;
print('国家: ${placemark.country}');
print('城市: ${placemark.locality}');
}
3.3 setLocaleIdentifier 方法
setLocaleIdentifier 方法用于设置语言环境,控制返回地址信息的语言。
Future<void> setLocaleIdentifier(String localeIdentifier)
参数说明:
localeIdentifier 参数是语言环境标识符,格式为 languageCode_countryCode。
语言环境格式:[languageCode]_[countryCode]
常用语言环境:
zh_CN:中文(中国)en_US:英文(美国)ja_JP:日文(日本)ko_KR:韩文(韩国)fr_FR:法文(法国)de_DE:德文(德国)
使用示例:
await _geocodingOhos.setLocaleIdentifier('zh_CN'); // 设置为中文环境
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(39.9042, 116.4074);
3.4 isPresent 方法
isPresent 方法用于检查地理编码服务是否可用。
Future<bool> isPresent()
返回类型:bool - 服务是否可用
使用示例:
bool available = await _geocodingOhos.isPresent();
if (available) {
// 地理编码服务可用
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区');
} else {
// 地理编码服务不可用
print('地理编码服务不可用');
}
3.5 Location 类
Location 表示地理位置信息。
class Location {
final double latitude; // 纬度
final double longitude; // 经度
final String? locality; // 位置名称
final String? country; // 国家
final String? administrativeArea; // 行政区
final String? subAdministrativeArea; // 子行政区
final String? thoroughfare; // 街道
final String? subThoroughfare; // 子街道
final String? postalCode; // 邮政编码
final String? isoCountryCode; // 国家代码
}
3.6 Placemark 类
Placemark 表示地址标记信息。
class Placemark {
final String? name; // 名称
final String? street; // 街道
final String? isoCountryCode; // 国家代码
final String? country; // 国家
final String? postalCode; // 邮政编码
final String? administrativeArea; // 行政区
final String? subAdministrativeArea; // 子行政区
final String? locality; // 城市
final String? subLocality; // 子城市
final String? thoroughfare; // 大道
final String? subThoroughfare; // 子大道
}
四、OpenHarmony 特有功能
4.1 地理编码服务
OpenHarmony 的地理编码服务支持多种查询方式:
正向地理编码:
- 支持模糊地址搜索
- 返回多个匹配结果
- 支持多语言地址
逆向地理编码:
- 支持精确坐标转地址
- 返回详细地址信息
- 支持多语言返回
4.2 多语言支持
OpenHarmony 地理编码服务支持多种语言:
// 中文环境
await _geocodingOhos.setLocaleIdentifier('zh_CN');
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(39.9042, 116.4074);
// 返回中文地址
// 英文环境
await _geocodingOhos.setLocaleIdentifier('en_US');
List<Placemark> placemarksEn = await _geocodingOhos.placemarkFromCoordinates(39.9042, 116.4074);
// 返回英文地址
4.3 地址格式
OpenHarmony 返回的地址信息包含完整的行政区划:
Placemark {
country: '中国',
administrativeArea: '北京市',
subAdministrativeArea: '海淀区',
locality: '北京市',
subLocality: '海淀区',
thoroughfare: '中关村大街',
subThoroughfare: '1号',
postalCode: '100080',
}
五、OpenHarmony 平台实现原理
5.1 原生 API 映射
geocoding 在 OpenHarmony 平台上使用 @kit.LocationKit 模块实现:
| Flutter API | OpenHarmony API |
|---|---|
| locationFromAddress | geoLocationManager.getAddressesFromLocationName |
| placemarkFromCoordinates | geoLocationManager.getAddressesFromLocation |
| setLocaleIdentifier | geoLocationManager.setLocale |
| isPresent | geoLocationManager.isGeocoderAvailable |
5.2 正向地理编码实现
OpenHarmony 使用 geoLocationManager.getAddressesFromLocationName 实现地址转坐标:
async function getAddressesFromLocationName(address: string): Promise<Location[]> {
let request: geoLocationManager.GeoAddressRequest = {
description: address,
maxItems: 5,
};
let geoAddresses = await geoLocationManager.getAddressesFromLocationName(request);
let locations: Location[] = [];
for (let geoAddress of geoAddresses) {
locations.push({
latitude: geoAddress.latitude,
longitude: geoAddress.longitude,
locality: geoAddress.locality,
country: geoAddress.countryName,
administrativeArea: geoAddress.administrativeArea,
subAdministrativeArea: geoAddress.subAdministrativeArea,
thoroughfare: geoAddress.thoroughfare,
subThoroughfare: geoAddress.subThoroughfare,
postalCode: geoAddress.postalCode,
isoCountryCode: geoAddress.countryCode,
});
}
return locations;
}
5.3 逆向地理编码实现
OpenHarmony 使用 geoLocationManager.getAddressesFromLocation 实现坐标转地址:
async function getAddressesFromLocation(latitude: number, longitude: number): Promise<Placemark[]> {
let request: geoLocationManager.ReverseGeoCodeRequest = {
latitude: latitude,
longitude: longitude,
maxItems: 5,
};
let geoAddresses = await geoLocationManager.getAddressesFromLocation(request);
let placemarks: Placemark[] = [];
for (let geoAddress of geoAddresses) {
placemarks.push({
name: geoAddress.placeName,
street: geoAddress.thoroughfare,
isoCountryCode: geoAddress.countryCode,
country: geoAddress.countryName,
postalCode: geoAddress.postalCode,
administrativeArea: geoAddress.administrativeArea,
subAdministrativeArea: geoAddress.subAdministrativeArea,
locality: geoAddress.locality,
subLocality: geoAddress.subLocality,
thoroughfare: geoAddress.thoroughfare,
subThoroughfare: geoAddress.subThoroughfare,
});
}
return placemarks;
}
5.4 多语言支持实现
OpenHarmony 使用 geoLocationManager.setLocale 设置语言环境:
async function setLocale(locale: string): Promise<void> {
await geoLocationManager.setLocale(locale);
}
5.5 服务状态检测实现
OpenHarmony 使用 geoLocationManager.isGeocoderAvailable 检查服务状态:
async function isGeocoderAvailable(): Promise<boolean> {
return await geoLocationManager.isGeocoderAvailable();
}
六、MethodChannel 通信协议
6.1 方法列表
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
locationFromAddress |
address | List <Location> |
地址转坐标 |
placemarkFromCoordinates |
latitude, longitude | List <Placemark> |
坐标转地址 |
setLocaleIdentifier |
localeIdentifier | void | 设置语言环境 |
isPresent |
- | bool | 检查服务状态 |
6.2 Location 数据格式
{
"latitude": 39.9042,
"longitude": 116.4074,
"locality": "北京市",
"country": "中国",
"administrativeArea": "北京市",
"subAdministrativeArea": "海淀区",
"thoroughfare": "中关村大街",
"subThoroughfare": "1号",
"postalCode": "100080",
"isoCountryCode": "CN"
}
6.3 Placemark 数据格式
{
"name": "中关村大厦",
"street": "中关村大街1号",
"isoCountryCode": "CN",
"country": "中国",
"postalCode": "100080",
"administrativeArea": "北京市",
"subAdministrativeArea": "海淀区",
"locality": "北京市",
"subLocality": "海淀区",
"thoroughfare": "中关村大街",
"subThoroughfare": "1号"
}
七、实战案例
7.1 基础地理编码功能
// 地址转坐标
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区中关村大街');
if (locations.isNotEmpty) {
Location location = locations.first;
print('纬度: ${location.latitude}');
print('经度: ${location.longitude}');
print('时间戳: ${location.timestamp}');
}
7.2 逆向地理编码
// 坐标转地址
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(39.9042, 116.4074);
if (placemarks.isNotEmpty) {
Placemark placemark = placemarks.first;
print('国家: ${placemark.country}');
print('省份: ${placemark.administrativeArea}');
print('城市: ${placemark.locality}');
print('街道: ${placemark.thoroughfare}');
}
7.3 多语言支持
// 设置语言环境
await _geocodingOhos.setLocaleIdentifier('zh-CN');
// 中文地址转坐标
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区中关村大街');
// 设置英文环境
await _geocodingOhos.setLocaleIdentifier('en-US');
// 英文地址转坐标
List<Location> locationsEn = await _geocodingOhos.locationFromAddress('Zhongguancun Street, Haidian District, Beijing');
7.4 服务状态检测
// 检查地理编码服务是否可用
bool isAvailable = await _geocodingOhos.isPresent();
if (isAvailable) {
print('地理编码服务可用');
} else {
print('地理编码服务不可用');
}
八、最佳实践
8.1 服务封装
class GeocodingService {
final GeocodingOhos _geocodingOhos = GeocodingOhos();
Future<Location?> getCoordinatesFromAddress(String address) async {
try {
List<Location> locations = await _geocodingOhos.locationFromAddress(address);
return locations.isNotEmpty ? locations.first : null;
} catch (e) {
print('地址转坐标失败: $e');
return null;
}
}
Future<Placemark?> getAddressFromCoordinates(double lat, double lng) async {
try {
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(lat, lng);
return placemarks.isNotEmpty ? placemarks.first : null;
} catch (e) {
print('坐标转地址失败: $e');
return null;
}
}
}
8.2 错误处理
Future<Location?> safeLocationFromAddress(String address) async {
final GeocodingOhos geocodingOhos = GeocodingOhos();
try {
bool available = await geocodingOhos.isPresent();
if (!available) {
print('地理编码服务不可用');
return null;
}
List<Location> locations = await geocodingOhos.locationFromAddress(address);
return locations.isNotEmpty ? locations.first : null;
} catch (e) {
print('地理编码失败: $e');
return null;
}
}
8.3 缓存策略
class GeocodingCache {
static final Map<String, Location> _addressCache = {};
static final Map<String, Placemark> _coordinateCache = {};
static final GeocodingOhos _geocodingOhos = GeocodingOhos();
static Future<Location?> getCachedLocation(String address) async {
if (_addressCache.containsKey(address)) {
return _addressCache[address];
}
List<Location> locations = await _geocodingOhos.locationFromAddress(address);
Location? location = locations.isNotEmpty ? locations.first : null;
if (location != null) {
_addressCache[address] = location;
}
return location;
}
static Future<Placemark?> getCachedPlacemark(double lat, double lng) async {
final key = '$lat,$lng';
if (_coordinateCache.containsKey(key)) {
return _coordinateCache[key];
}
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(lat, lng);
Placemark? placemark = placemarks.isNotEmpty ? placemarks.first : null;
if (placemark != null) {
_coordinateCache[key] = placemark;
}
return placemark;
}
static void clearCache() {
_addressCache.clear();
_coordinateCache.clear();
}
}
8.4 地址格式化
String formatAddress(Placemark placemark) {
List<String> parts = [];
if (placemark.country != null) parts.add(placemark.country!);
if (placemark.administrativeArea != null) parts.add(placemark.administrativeArea!);
if (placemark.locality != null) parts.add(placemark.locality!);
if (placemark.thoroughfare != null) parts.add(placemark.thoroughfare!);
if (placemark.subThoroughfare != null) parts.add(placemark.subThoroughfare!);
return parts.join(' ');
}
8.5 批量查询优化
Future<List<Location>> batchGeocodeAddresses(List<String> addresses) async {
List<Location> results = [];
for (String address in addresses) {
Location? location = await GeocodingCache.getCachedLocation(address);
if (location != null) {
results.add(location);
}
// 避免请求过快
await Future.delayed(const Duration(milliseconds: 100));
}
return results;
}
九、常见问题
Q1:地理编码失败怎么办?
检查以下几点:
-
确保权限已授予
// 地理编码需要位置权限 -
检查服务是否可用
bool available = await _geocodingOhos.isPresent();
if (!available) {
print(‘地理编码服务不可用’);
}
3. **确保地址格式正确**
```dart
// 使用标准地址格式
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区中关村大街1号');
Q2:地址解析不准确怎么办?
提高地址精度:
// 使用更详细的地址
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区中关村大街1号中关村大厦');
// 从多个结果中选择最匹配的
if (locations.length > 1) {
// 根据业务逻辑选择最合适的结果
Location bestMatch = locations.first;
}
Q3:如何处理多语言地址?
设置对应的语言环境:
// 处理中文地址
await _geocodingOhos.setLocaleIdentifier('zh_CN');
List<Location> locations = await _geocodingOhos.locationFromAddress('北京市海淀区中关村大街');
// 处理英文地址
await _geocodingOhos.setLocaleIdentifier('en_US');
List<Location> locationsEn = await _geocodingOhos.locationFromAddress('Zhongguancun Street, Beijing');
Q4:如何提高查询性能?
使用缓存策略:
// 使用缓存避免重复查询
Location? location = await GeocodingCache.getCachedLocation('北京市海淀区中关村大街');
Q5:如何处理无效坐标?
验证坐标范围:
Future<Placemark?> safePlacemarkFromCoordinates(double lat, double lng) async {
// 验证坐标范围
if (lat < -90 || lat > 90 || lng < -180 || lng > 180) {
print('无效的坐标');
return null;
}
return await GeocodingService().getAddressFromCoordinates(lat, lng);
}
Q6:如何获取更详细的地址信息?
使用完整的 Placemark 对象:
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(39.9042, 116.4074);
if (placemarks.isNotEmpty) {
Placemark placemark = placemarks.first;
print('国家: ${placemark.country}');
print('省份: ${placemark.administrativeArea}');
print('城市: ${placemark.locality}');
print('区县: ${placemark.subAdministrativeArea}');
print('街道: ${placemark.thoroughfare}');
print('门牌号: ${placemark.subThoroughfare}');
print('邮编: ${placemark.postalCode}');
}
十、总结
geocoding 库为 Flutter for OpenHarmony 开发提供了完整的地理编码能力。通过丰富的 API,开发者可以实现地址转坐标、坐标转地址等功能。该库在鸿蒙平台上已经完成了完整的适配,支持所有核心功能,包括多语言支持和详细地址信息,开发者可以放心使用。
核心特性:
- 完整功能:支持正向地理编码、逆向地理编码等完整的地理编码功能
- 跨平台一致:API 设计与 Android、iOS 平台保持一致,降低学习成本
- 多语言支持:支持设置不同语言环境,返回对应语言的地址信息
- 易于集成:提供清晰的 API 和丰富的示例代码
使用建议:
- 在使用前先检查服务是否可用
- 使用缓存策略提高性能
- 处理好错误和异常情况
- 根据业务需求选择合适的语言环境
结合 location 库使用:
geocoding 库可以与 location 库配合使用,实现更完整的位置服务功能,如地址搜索、位置标注、导航规划等,为用户提供全方位的位置服务体验。
十一、完整代码示例
以下是一个完整的可运行示例,展示了 geocoding 库的核心功能:
main.dart
import 'package:flutter/material.dart';
import 'package:geocoding_ohos/geocoding_ohos.dart';
import 'package:geocoding_platform_interface/geocoding_platform_interface.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Geocoding Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController _addressController = TextEditingController();
final TextEditingController _latController = TextEditingController();
final TextEditingController _lngController = TextEditingController();
final GeocodingOhos _geocodingOhos = GeocodingOhos();
List<Location> _locations = [];
List<Placemark> _placemarks = [];
String? _errorMessage;
bool _isLoading = false;
String _currentLocale = 'zh_CN';
void initState() {
super.initState();
_checkService();
}
Future<void> _checkService() async {
bool available = await _geocodingOhos.isPresent();
if (!available) {
setState(() {
_errorMessage = '地理编码服务不可用';
});
}
}
void _showMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
Future<void> _geocodeAddress() async {
final address = _addressController.text.trim();
if (address.isEmpty) {
setState(() {
_errorMessage = '请输入地址';
});
return;
}
setState(() {
_isLoading = true;
_errorMessage = null;
_locations.clear();
});
try {
// 检查服务是否可用
bool available = await _geocodingOhos.isPresent();
if (!available) {
setState(() {
_errorMessage = '地理编码服务不可用';
_isLoading = false;
});
return;
}
// 设置语言环境
await _geocodingOhos.setLocaleIdentifier(_currentLocale);
// 搜索地址
List<Location> locations = await _geocodingOhos.locationFromAddress(address);
setState(() {
_locations = locations;
_isLoading = false;
});
_showMessage('找到 ${locations.length} 个结果');
} catch (e) {
setState(() {
_errorMessage = '搜索失败: $e';
_isLoading = false;
});
_showMessage('搜索失败');
}
}
Future<void> _reverseGeocode() async {
final lat = double.tryParse(_latController.text);
final lng = double.tryParse(_lngController.text);
if (lat == null || lng == null) {
setState(() {
_errorMessage = '请输入有效的经纬度';
});
return;
}
setState(() {
_isLoading = true;
_errorMessage = null;
_placemarks.clear();
});
try {
// 检查服务是否可用
bool available = await _geocodingOhos.isPresent();
if (!available) {
setState(() {
_errorMessage = '地理编码服务不可用';
_isLoading = false;
});
return;
}
// 设置语言环境
await _geocodingOhos.setLocaleIdentifier(_currentLocale);
// 坐标转地址
List<Placemark> placemarks = await _geocodingOhos.placemarkFromCoordinates(lat, lng);
setState(() {
_placemarks = placemarks;
_isLoading = false;
});
_showMessage('找到 ${placemarks.length} 个结果');
} catch (e) {
setState(() {
_errorMessage = '逆地理编码失败: $e';
_isLoading = false;
});
_showMessage('逆地理编码失败');
}
}
void _changeLocale(String locale) {
setState(() {
_currentLocale = locale;
});
_showMessage('语言环境已切换');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('地理编码示例'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
actions: [
PopupMenuButton<String>(
onSelected: _changeLocale,
itemBuilder: (context) => [
const PopupMenuItem(
value: 'zh_CN',
child: Text('中文'),
),
const PopupMenuItem(
value: 'en_US',
child: Text('英文'),
),
const PopupMenuItem(
value: 'ja_JP',
child: Text('日文'),
),
],
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
// 正向地理编码部分
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('正向地理编码', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
TextField(
controller: _addressController,
decoration: const InputDecoration(
labelText: '输入地址',
hintText: '例如:北京市海淀区中关村大街1号',
border: OutlineInputBorder(),
),
onSubmitted: (_) => _geocodeAddress(),
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _isLoading ? null : _geocodeAddress,
icon: const Icon(Icons.search),
label: const Text('地址转坐标'),
),
if (_locations.isNotEmpty) ...[
const SizedBox(height: 16),
const Text('结果:', style: TextStyle(fontWeight: FontWeight.bold)),
..._locations.map((location) => ListTile(
leading: const CircleAvatar(child: Icon(Icons.location_on)),
title: Text('纬度: ${location.latitude?.toStringAsFixed(6)}, 经度: ${location.longitude?.toStringAsFixed(6)}'),
)),
],
],
),
),
),
const SizedBox(height: 16),
// 逆向地理编码部分
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('逆向地理编码', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextField(
controller: _latController,
decoration: const InputDecoration(
labelText: '纬度',
hintText: '例如:39.9042',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
controller: _lngController,
decoration: const InputDecoration(
labelText: '经度',
hintText: '例如:116.4074',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
],
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _isLoading ? null : _reverseGeocode,
icon: const Icon(Icons.my_location),
label: const Text('坐标转地址'),
),
if (_placemarks.isNotEmpty) ...[
const SizedBox(height: 16),
const Text('结果:', style: TextStyle(fontWeight: FontWeight.bold)),
..._placemarks.map((placemark) => ListTile(
leading: const CircleAvatar(child: Icon(Icons.place)),
title: Text('${placemark.country ?? ""} ${placemark.administrativeArea ?? ""}'),
subtitle: Text('${placemark.locality ?? ""} ${placemark.thoroughfare ?? ""}'),
)),
],
],
),
),
),
if (_errorMessage != null) ...[
const SizedBox(height: 16),
Card(
color: Colors.red.shade50,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Icon(Icons.error, color: Colors.red),
const SizedBox(width: 8),
Expanded(child: Text(_errorMessage!, style: const TextStyle(color: Colors.red))),
],
),
),
),
],
if (_isLoading) ...[
const SizedBox(height: 16),
const Center(child: CircularProgressIndicator()),
],
],
),
),
);
}
}
更多推荐


所有评论(0)