mcpField
M
mapbox-flutter-patterns

mapbox-flutter-patterns

github.com/mapbox开发83794 次安装4 周前更新
代码仓库

关于 mapbox-flutter-patterns

mapbox-flutter-patterns 是面向 Flutter 开发者的 Mapbox Maps SDK 集成指南,适用于 iOS 和 Android。内容涵盖配置、地图控制、注记、位置显示和常见构建问题。

主要功能

  1. 01Mapbox Flutter 安装与令牌配置
  2. 02MapWidget 初始化和相机控制
  3. 03点、圆、线和多边形注记
  4. 04注记点击与手势事件处理
  5. 05加载 GeoJSON 并显示用户位置
  6. 06iOS 和 Android 配置故障排查

适用场景

  1. 01在 Flutter 应用中加入交互式 Mapbox 地图
  2. 02显示标记或 GeoJSON 要素
  3. 03显示用户位置并响应注记点击
  4. 04排查集成 Mapbox 后的 iOS 构建失败

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-flutter-patterns

安装后在 Claude Code 中输入/mapbox-flutter-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AGENTS.md
evals/
evals.json
references/
annotations.md
platform-setup.md

详细介绍

Mapbox Flutter 集成模式

在 iOS 和 Android 上使用 Dart 集成 Mapbox Maps SDK for Flutter(mapbox_maps_flutter)的官方模式。

在以下情况下使用此技能:

  • 在 Flutter 应用中安装和配置 mapbox_maps_flutter
  • 正确设置 Mapbox 访问令牌
  • 使用相机/样式选项初始化 MapWidget
  • 添加注释(点、圆、线、多边形)并处理点击
  • 显示用户位置图标
  • 从应用资源加载 GeoJSON
  • 添加 Mapbox 后排查 iOS 构建失败问题

官方资源:

不支持 Web 和桌面端 — Flutter SDK 仅面向 iOS 和 Android。


安装与设置

要求
  • Flutter SDK 3.22.3 / Dart 3.4.4+
  • iOS:部署目标为 14.0 或更高版本
  • Android:minSdk 为 21 或更高版本
  • 免费 Mapbox 账户
步骤 1:添加依赖项
# pubspec.yaml
dependencies:
  mapbox_maps_flutter: ^2.0.0
flutter pub get
步骤 2:将 iOS 部署目标提升到 14.0(必需)

这是添加 Mapbox 后导致 iOS 构建失败的最常见原因。 Flutter SDK 要求 iOS 14.0,使用 Flutter 默认值将无法编译。

  1. 在 Xcode 中打开 ios/Runner.xcworkspace。

  2. 选择 Runner 目标 → General → 将 Minimum Deployments → iOS 设置为 14.0。

  3. 如果存在 ios/Podfile,也要更新其中的平台行:

    # ios/Podfile
    platform :ios, '14.0'
    

无需担心使用 CocoaPods 还是 Swift Package Manager — mapbox_maps_flutter 同时支持两者,Flutter 会根据应用的配置选择相应方式。

步骤 3:iOS 位置权限

将用途说明字符串添加到 ios/Runner/Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Show your location on the map</string>
步骤 4:Android 权限

添加到 android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
步骤 5:配置访问令牌

推荐的做法是在构建/运行时通过 --dart-define 传递令牌,并在创建任何 MapWidget 之前将其设置到 MapboxOptions 上。

flutter run --dart-define=ACCESS_TOKEN=pk.your_token_here
// main.dart
import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

const accessToken = String.fromEnvironment('ACCESS_TOKEN');

void main() {
  MapboxOptions.setAccessToken(accessToken);
  runApp(const MaterialApp(home: MapScreen()));
}

切勿在源代码中硬编码令牌。在 CI 中,传入 --dart-define=ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN。


地图初始化

基础地图
import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

class MapScreen extends StatelessWidget {
  const MapScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        key: const ValueKey('mapWidget'),
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-122.4194, 37.7749)),
          zoom: 12,
        ),
        styleUri: MapboxStyles.STANDARD,
      ),
    );
  }
}
获取 MapboxMap 控制器
class MapScreen extends StatefulWidget {
  const MapScreen({super.key});

  @override
  State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  MapboxMap? mapboxMap;

  void _onMapCreated(MapboxMap controller) {
    mapboxMap = controller;
  }

  @override
  Widget build(BuildContext context) {
    return MapWidget(
      key: const ValueKey('mapWidget'),
      onMapCreated: _onMapCreated,
      cameraOptions: CameraOptions(
        center: Point(coordinates: Position(-122.4194, 37.7749)),
        zoom: 12,
      ),
    );
  }
}

添加注释

使用 mapboxMap.annotations 为点、圆、折线和多边形注释创建管理器。管理器会长期存在 — 创建一次并重复使用,以便更新。

使用自定义图像的点注释
import 'package:flutter/services.dart' show rootBundle;

PointAnnotationManager? pointAnnotationManager;

Future<void> _addMarkers(MapboxMap mapboxMap) async {
  pointAnnotationManager = await mapboxMap.annotations.createPointAnnotationManager();

  final bytes = await rootBundle.load('assets/marker.png');
  final imageBytes = bytes.buffer.asUint8List();

  final options = <PointAnnotationOptions>[
    PointAnnotationOptions(
      geometry: Point(coordinates: Position(-122.4194, 37.7749)),
      image: imageBytes,
      iconSize: 1.2,
    ),
    PointAnnotationOptions(
      geometry: Point(coordinates: Position(-122.4094, 37.7849)),
      image: imageBytes,
    ),
  ];

  await pointAnnotationManager!.createMulti(options);
}

请记得在 pubspec.yaml 中注册资源:

flutter:
  assets:
    - assets/marker.png
点击处理

使用 manager.tapEvents — 这是当前 API。addOnPointAnnotationClickListener 已弃用。

tapEvents 会返回一个 Cancelable,请保存该对象,并在不再需要监听器时调用 .cancel():

final Cancelable tapSubscription = pointAnnotationManager!.tapEvents(
  onTap: (annotation) {
    debugPrint('Tapped annotation ${annotation.id}');
  },
);

@override
void dispose() {
  tapSubscription.cancel();
  super.dispose();
}

每个管理器的 longPressEvents 和 dragEvents,以及其他注释类型(CircleAnnotationManager.tapEvents 等)都采用相同模式 — 返回一个 Cancelable。

从 GeoJSON 加载注释
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;

Future<void> _loadGeoJson(MapboxMap mapboxMap) async {
  final raw = await rootBundle.loadString('assets/coffee_shops.geojson');
  final geo = jsonDecode(raw) as Map<String, dynamic>;
  final features = (geo['features'] as List).cast<Map<String, dynamic>>();

  final manager = await mapboxMap.annotations.createPointAnnotationManager();
  final icon = (await rootBundle.load('assets/coffee.png')).buffer.asUint8List();

  final options = features.map((feature) {
    final coords = feature['geometry']['coordinates'] as List;
    return PointAnnotationOptions(
      geometry: Point(coordinates: Position(coords[0] as double, coords[1] as double)),
      image: icon,
    );
  }).toList();

  await manager.createMulti(options);
}

对于数千个要素,请使用样式图层(GeoJsonSource + SymbolLayer),而不是注释。


显示用户位置

启用位置图标之前,必须已授予权限(使用 permission_handler 或类似工具)。

await mapboxMap.location.updateSettings(LocationComponentSettings(
  enabled: true,
  puckBearingEnabled: true,
  locationPuck: LocationPuck(
    locationPuck2D: DefaultLocationPuck2D(),
  ),
));

相机控制

// Instant jump
await mapboxMap.setCamera(CameraOptions(
  center: Point(coordinates: Position(-80.1263, 25.7845)),
  zoom: 14,
));

// Animated fly-to
await mapboxMap.flyTo(
  CameraOptions(
    center: Point(coordinates: Position(-80.1263, 25.7845)),
    zoom: 17,
    bearing: 180,
    pitch: 30,
  ),
  MapAnimationOptions(duration: 2000),
);

故障排查

iOS 构建失败,提示“platform is lower than deployment target”

Flutter 默认的 iOS 部署目标低于 Mapbox 的最低要求(iOS 14)。在 Xcode 的 Runner 目标中,将 Minimum Deployments → iOS 设置为 14.0。如果项目中存在 ios/Podfile,也要将其中的平台设置为 platform :ios, '14.0',然后重新运行 pod install。

未调用 setAccessToken

如果忘记在创建 MapWidget 之前调用 MapboxOptions.setAccessToken,地图会显示空白网格。务必在 main() 中、runApp 之前调用它。

注释点击处理器未触发

确保使用的是 manager.tapEvents(onTap: ...)——addOnPointAnnotationClickListener 已弃用。另外,请确认在创建注释管理器之前,已通过 onMapCreated 获取 MapboxMap 控制器。

更改权限后热重载

iOS/Android 不会在热重载时重新读取清单或 Info.plist。编辑权限后,请完全重启应用。


参考文件

  • references/annotations.md — 圆形、折线、多边形模式,以及 GeoJSON 源/图层配置示例。
  • references/platform-setup.md — 更深入的 iOS/Android 配置、令牌策略和发布签名说明。

其他资源

评论

支持 Markdown