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

基于Flutter Map与MapBox API实现城市/区域轮廓展示及指定区域标记方法咨询(非Google Maps)

嘿,我来帮你搞定这个问题!之前我也折腾过类似的需求,刚好有经验可以分享给你。下面是用Flutter Map结合MapBox API实现城市/区域轮廓展示、标记指定范围的完整方案:

用Flutter Map + MapBox API实现区域轮廓展示与标记

第一步:前期准备

  • 先去MapBox官网注册账号,获取专属的access token——所有MapBox API请求都依赖这个令牌,别弄丢了。
  • 在Flutter项目的pubspec.yaml里添加必要依赖:
dependencies:
  flutter_map: ^6.1.0
  http: ^1.1.0
  latlong2: ^0.9.0  # flutter_map依赖的经纬度处理库

第二步:获取区域轮廓的GeoJSON数据

MapBox的Geocoding API可以通过城市名称或经纬度,返回包含区域轮廓的GeoJSON数据,核心是要加上geometry=polygon参数才能拿到轮廓坐标。

方式1:通过城市名称获取轮廓

发送GET请求到MapBox Geocoding API,指定城市类型和返回多边形数据:

import 'package:http/http.dart' as http;
import 'dart:convert';

Future<Map<String, dynamic>> fetchCityOutline(String cityName) async {
  final accessToken = '你的MapBox Access Token';
  final url = Uri.parse(
    'https://api.mapbox.com/geocoding/v5/mapbox.places/$cityName.json?access_token=$accessToken&types=city&geometry=polygon&limit=1'
  );
  
  final response = await http.get(url);
  if (response.statusCode == 200) {
    return json.decode(response.body);
  } else {
    throw Exception('加载城市轮廓失败,请检查令牌或网络');
  }
}

返回结果里的features[0].geometry.coordinates就是城市轮廓的经纬度数组。

方式2:通过经纬度反查所属区域轮廓

如果手里只有经纬度,想知道它对应的区域轮廓,只需把经纬度作为参数传入API:

Future<Map<String, dynamic>> fetchOutlineByLatLng(double lat, double lng) async {
  final accessToken = '你的MapBox Access Token';
  // 注意MapBox的经纬度顺序是[经度, 纬度]
  final url = Uri.parse(
    'https://api.mapbox.com/geocoding/v5/mapbox.places/$lng,$lat.json?access_token=$accessToken&types=city&geometry=polygon&limit=1'
  );
  
  final response = await http.get(url);
  if (response.statusCode == 200) {
    return json.decode(response.body);
  } else {
    throw Exception('根据经纬度加载轮廓失败');
  }
}

第三步:在Flutter Map上渲染轮廓

拿到GeoJSON坐标后,需要转换成flutter_map能识别的格式,再通过图层渲染出来。

坐标格式转换

MapBox返回的坐标是[经度, 纬度],但latlong2库用的是[纬度, 经度],必须反转:

import 'package:latlong2/latlong.dart';

List<LatLng> convertGeoJsonToLatLng(List<dynamic> coordinates) {
  return (coordinates[0] as List<dynamic>).map((coord) {
    return LatLng(coord[1], coord[0]);
  }).toList();
}

完整渲染示例

用PolygonLayer展示填充区域,PolylineLayer展示轮廓边界:

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  List<LatLng>? _cityOutline;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _loadCityOutline('Shanghai'); // 替换成你要查询的城市
  }

  Future<void> _loadCityOutline(String cityName) async {
    setState(() => _isLoading = true);
    try {
      final data = await fetchCityOutline(cityName);
      final coordinates = data['features'][0]['geometry']['coordinates'];
      setState(() {
        _cityOutline = convertGeoJsonToLatLng(coordinates);
        _isLoading = false;
      });
    } catch (e) {
      setState(() => _isLoading = false);
      print('错误信息:$e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : FlutterMap(
              options: MapOptions(
                center: _cityOutline?.first ?? const LatLng(31.2304, 121.4737), // 默认上海坐标
                zoom: 10,
              ),
              children: [
                // MapBox瓦片底图
                TileLayer(
                  urlTemplate: 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token={accessToken}',
                  additionalOptions: {
                    'accessToken': '你的MapBox Access Token',
                  },
                ),
                // 渲染城市轮廓填充与边界
                if (_cityOutline != null)
                  PolygonLayer(
                    polygons: [
                      Polygon(
                        points: _cityOutline!,
                        color: Colors.green.withOpacity(0.3),
                        borderColor: Colors.green,
                        borderStrokeWidth: 2,
                      ),
                    ],
                  ),
              ],
            ),
    );
  }
}

第四步:标记指定地点的区域范围

如果要标记商圈、小区这类更细分的区域,流程和上面一致:

  1. 若有地点名称,调用Geocoding API时指定更精确的types参数(比如poi、neighborhood)
  2. 若有自定义范围的经纬度列表,直接传入PolygonLayer即可
  3. 同样可以用PolylineLayer单独渲染边界,或者PolygonLayer做填充标记

关键注意事项

  • MapBox免费版有调用限额,测试够用,上线前记得检查配额
  • 坐标顺序一定要反转,否则地图上的位置会完全错乱
  • 如果需要更精细的行政区边界,可尝试MapBox的Boundaries API(需要更高权限),或者使用公开的GeoJSON数据源(注意版权问题)

内容的提问来源于stack exchange,提问作者Nil Martinez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 21:42:35