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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 08:54:52