如何将Flutter在线地理编码代码改造为离线版及相关地图功能
离线环境下实现地名定位、路线绘制与距离计算的Flutter方案
核心改造方向
- 替换在线地理编码:使用本地预存的地名-经纬度映射(JSON/SQLite存储)
- 选用离线友好的地图组件:
flutter_map(替代依赖在线服务的Google Maps) - 离线路线与距离计算:通过
latlong2库实现直线距离计算,简化路线为两点连线(如需路网路线需额外引入离线路网数据)
步骤1:准备离线地名数据
在assets目录下创建offline_locations.json,预存常用地名与对应经纬度:
{ "北京天安门": {"lat": 39.9042, "lng": 116.4074}, "上海外滩": {"lat": 31.2304, "lng": 121.4737}, "广州塔": {"lat": 23.1063, "lng": 113.3245} }
记得在pubspec.yaml中声明资源:
flutter: assets: - assets/offline_locations.json
步骤2:添加依赖
在pubspec.yaml中引入离线开发所需的包:
dependencies: flutter_map: ^6.1.0 latlong2: ^0.9.0 location: ^5.0.3
步骤3:实现离线地理编码函数
编写本地查询逻辑,替代原在线地理编码接口:
import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:latlong2/latlong.dart'; Future<LatLng?> offlineGeocode(String locationName) async { // 读取本地地名数据 final jsonString = await rootBundle.loadString('assets/offline_locations.json'); final Map<String, dynamic> locations = json.decode(jsonString); // 精确匹配 if (locations.containsKey(locationName)) { final data = locations[locationName]; return LatLng(data['lat'], data['lng']); } // 模糊匹配(可选) for (var entry in locations.entries) { if (entry.key.contains(locationName)) { final data = entry.value; return LatLng(data['lat'], data['lng']); } } return null; }
步骤4:改造提交函数,添加路线与距离计算
先定义全局变量存储位置与路线数据:
LatLng? _currentLocation; LatLng? _destinationLocation; List<LatLng> _routePoints = []; double? _distance; MapController? mapController;
然后替换原_onSubmitLocation函数:
void _onSubmitLocation() async { final String enteredLocation = locationName.text; if (enteredLocation.isNotEmpty) { try { // 离线查询地名对应的经纬度 final LatLng? newLocation = await offlineGeocode(enteredLocation); if (newLocation != null) { _destinationLocation = newLocation; // 移动地图相机到目的地 mapController?.animateCamera(CameraUpdate.newLatLngZoom(newLocation, 14)); // 计算并显示距离、绘制路线(仅当获取到当前位置时) if (_currentLocation != null) { final distanceCalculator = Distance(); _distance = distanceCalculator.as(LengthUnit.Kilometer, _currentLocation!, newLocation); // 绘制直线路线(如需路网路线需引入离线路网数据集) _routePoints = [_currentLocation!, newLocation]; setState(() {}); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('距离:${_distance!.toStringAsFixed(2)}公里'), backgroundColor: Theme.of(context).colorScheme.primary, duration: const Duration(seconds: 3), )); } else { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('已定位到目的地'), backgroundColor: Theme.of(context).colorScheme.primary, duration: Duration(seconds: 3), )); } } else { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('未找到该地点'), backgroundColor: Colors.red, duration: Duration(seconds: 3), )); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('查询地点出错'), backgroundColor: Colors.red, duration: Duration(seconds: 3), )); } } }
步骤5:实现离线地图与位置获取
在页面中添加flutter_map组件,并实现离线当前位置获取:
// 获取当前位置(离线模式) Future<void> _getCurrentLocation() async { final Location location = Location(); final PermissionStatus permission = await location.hasPermission(); if (permission == PermissionStatus.denied) { await location.requestPermission(); } final LocationData data = await location.getLocation(); _currentLocation = LatLng(data.latitude!, data.longitude!); setState(() {}); } // 地图组件 FlutterMap( mapController: mapController, options: MapOptions( initialCenter: const LatLng(39.9042, 116.4074), initialZoom: 14, ), children: [ // 使用离线瓦片(需提前预下载地图瓦片到本地) TileLayer( urlTemplate: 'assets/map_tiles/{z}/{x}/{y}.png', ), // 路线图层 PolylineLayer( polylines: [ Polyline( points: _routePoints, color: Colors.blue, strokeWidth: 4, ), ], ), // 标记图层 MarkerLayer( markers: [ if (_currentLocation != null) Marker( point: _currentLocation!, child: const Icon(Icons.my_location, color: Colors.red, size: 30), ), if (_destinationLocation != null) Marker( point: _destinationLocation!, child: const Icon(Icons.location_on, color: Colors.green, size: 30), ), ], ), ], )
内容的提问来源于stack exchange,提问作者Hacker King
相关产品推荐
相关产品推荐

