Flutter地图展示插件mapbox_maps_flutter的使用

Flutter地图展示插件 mapbox_maps_flutter 的使用

概述

Mapbox Maps SDK Flutter SDK 是由 Mapbox 官方开发的解决方案,允许开发者在 Android 和 iOS 应用中嵌入高度定制的地图。该 SDK 支持最新的 Mapbox Maps SDK 版本(v11.9.0),并且提供了丰富的功能。

注意:Web 和桌面平台不支持此 SDK。

支持的 API

以下是 SDK 支持的功能列表:

功能 Android iOS
样式
相机位置
相机动画
事件
手势
用户位置
圆形图层
填充图层
填充挤出图层
线条图层
光栅图层
符号图层
山影图层
热力图层
天空图层
GeoJson 数据源
图像数据源
矢量数据源
光栅数据源
光栅dem数据源
圆形注释
点注释
线注释
填充注释
快照工具
离线模式

要求

  • iOS 12 或更高版本
  • Android SDK 21 或更高版本
  • Flutter SDK 3.22.3/Dart SDK 3.4.4 或更高版本

安装

配置凭证

为了运行 Mapbox Maps Flutter SDK,你需要配置 Mapbox Access Token。你可以通过以下方式设置:

MapboxOptions.setAccessToken(ACCESS_TOKEN);

可以通过命令行参数传递 Access Token:

flutter build <platform> --dart-define ACCESS_TOKEN=...

或者在运行应用时传递:

flutter run --dart-define ACCESS_TOKEN=...

也可以在 launch.json 中持久化存储 token:

"configurations": [
    {
        ...
        "args": [
            "--dart-define", "ACCESS_TOKEN=..."
        ],
    }
]

然后在应用中从环境变量中获取 token:

String ACCESS_TOKEN = String.fromEnvironment("ACCESS_TOKEN");

添加依赖

pubspec.yaml 文件中添加依赖:

dependencies:
  mapbox_maps_flutter: ^2.5.0

配置权限

你需要授予位置权限才能使用 Mapbox Maps Flutter SDK 的位置组件。可以使用现有的库来请求位置权限,例如 permission_handler

await Permission.locationWhenInUse.request();

还需要在两个平台上声明权限:

Android

AndroidManifest.xml 中添加以下权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS

Runner/Info.plist 中添加以下键值对,并解释为什么需要访问位置:

<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>

添加地图

导入 mapbox_maps_flutter 库并添加一个简单的地图:

import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  runApp(MaterialApp(home: MapWidget()));
}

MapWidget Widget

MapWidget 提供了自定义地图的选项,可以设置 MapOptionsCameraOptionsstyleURL 等。

MapboxMap 控制器

MapboxMap 控制器实例通过 MapWidget.onMapCreated 回调提供。它允许控制地图、相机、样式等。

示例代码:

class FullMap extends StatefulWidget {
  const FullMap();

  @override
  State createState() => FullMapState();
}

class FullMapState extends State<FullMap> {
  MapboxMap? mapboxMap;

  _onMapCreated(MapboxMap mapboxMap) {
    this.mapboxMap = mapboxMap;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: MapWidget(
      key: ValueKey("mapWidget"),
      onMapCreated: _onMapCreated,
      cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-80.1263, 25.7845)).toJson(),
          zoom: 12.0),
    ));
  }
}

用户位置

要观察用户的当前位置并在地图上显示位置指示器,可以使用 MapboxMap.location

示例代码:

mapboxMap.location.updateSettings(LocationComponentSettings(
    locationPuck: LocationPuck(
        locationPuck3D: LocationPuck3D(
            modelUri:
                "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf"))));

标记和注释

有几种方法可以在地图上添加注释:

  1. 使用 AnnotationManager APIs 创建圆形、点、多边形和折线注释。
  2. 使用样式图层。

示例代码:

mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async {
  final ByteData bytes = await rootBundle.load('assets/symbols/custom-icon.png');
  final Uint8List list = bytes.buffer.asUint8List();
  var options = <PointAnnotationOptions>[];
  for (var i = 0; i < 5; i++) {
    options.add(PointAnnotationOptions(
        geometry: createRandomPoint().toJson(), image: list));
  }
  pointAnnotationManager?.createMulti(options);
});

地图样式

可以通过 MapWidget.styleUri 设置初始样式 URI,或在运行时使用 MapboxMap.loadStyleURI 加载样式:

mapboxMap.loadStyleURI(Styles.LIGHT);

示例代码:

var data = await rootBundle.loadString('assets/polyline.geojson');
await mapboxMap.style.addSource(GeoJsonSource(id: "line", data: data));
await mapboxMap.style.addLayer(LineLayer(
    id: "line_layer",
    sourceId: "line",
    lineJoin: LineJoin.ROUND,
    lineCap: LineCap.ROUND,
    lineOpacity: 0.7,
    lineColor: Colors.red.value,
    lineWidth: 8.0));

相机和动画

可以设置起始相机位置:

MapWidget(
  key: ValueKey("mapWidget"),
  cameraOptions: CameraOptions(
      center: Point(coordinates: Position(-80.1263, 25.7845)).toJson(),
      zoom: 12.0),
);

或在运行时更新相机位置:

MapboxMap.setCamera(CameraOptions(
  center: Point(coordinates: Position(-80.1263, 25.7845)).toJson(),
  zoom: 12.0));

启动 flyTo 动画:

mapboxMap?.flyTo(
  CameraOptions(
      anchor: ScreenCoordinate(x: 0, y: 0),
      zoom: 17,
      bearing: 180,
      pitch: 30),
  MapAnimationOptions(duration: 2000, startDelay: 0));

用户交互

用户可以通过标准手势与地图进行交互。可以检索或更新 GestureSettings 使用 MapboxMap.gestures

示例代码:

MapWidget(
  key: ValueKey("mapWidget"),
  onTapListener: (point) {
    print("Tapped at $point");
  },
  onLongTapListener: (point) {
    print("Long tapped at $point");
  },
  onScrollListener: (delta) {
    print("Scrolled by $delta");
  },
);

完整示例 Demo:

import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MapboxOptions.setAccessToken("YOUR_ACCESS_TOKEN");
  runApp(MaterialApp(home: MapsDemo()));
}

class MapsDemo extends StatelessWidget {
  static const String ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";

  void _pushPage(BuildContext context, Example page) async {
    Navigator.of(context).push(MaterialPageRoute<void>(
        builder: (_) => Scaffold(
              appBar: AppBar(title: Text(page.title)),
              body: page,
            )));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('MapboxMaps examples')),
      body: ACCESS_TOKEN.isEmpty || ACCESS_TOKEN.contains("YOUR_TOKEN")
          ? buildAccessTokenWarning()
          : ListView.separated(
              itemCount: _allPages.length,
              separatorBuilder: (_, __) => const Divider(height: 1),
              itemBuilder: (_, int index) {
                final example = _allPages[index];
                return ListTile(
                  leading: example.leading,
                  title: Text(example.title),
                  subtitle: (example.subtitle?.isNotEmpty == true)
                      ? Text(
                          example.subtitle!,
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                        )
                      : null,
                  onTap: () => _pushPage(context, _allPages[index]),
                );
              },
            ),
    );
  }

  Widget buildAccessTokenWarning() {
    return Container(
      color: Colors.red[900],
      child: SizedBox.expand(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            "Please pass in your access token with",
            "--dart-define=ACCESS_TOKEN=ADD_YOUR_TOKEN_HERE",
            "passed into flutter run or add it to args in vscode's launch.json",
          ]
              .map((text) => Padding(
                    padding: EdgeInsets.all(8),
                    child: Text(text,
                        textAlign: TextAlign.center,
                        style: TextStyle(
                            fontSize: 14,
                            fontWeight: FontWeight.bold,
                            color: Colors.white)),
                  ))
              .toList(),
        ),
      ),
    );
  }
}

请确保将 YOUR_ACCESS_TOKEN 替换为你的实际 Mapbox Access Token。


更多关于Flutter地图展示插件mapbox_maps_flutter的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地图展示插件mapbox_maps_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter应用中使用mapbox_maps_flutter插件来展示地图的示例代码。这个示例展示了如何设置Mapbox地图,并加载一个基本的地图视图。

首先,你需要在你的pubspec.yaml文件中添加mapbox_maps_flutter依赖:

dependencies:
  flutter:
    sdk: flutter
  mapbox_maps_flutter: ^0.14.0  # 请检查最新版本号并替换

然后,运行flutter pub get来安装依赖。

接下来,你需要配置Mapbox的访问令牌。你可以在Mapbox官网创建一个账户,并获取一个访问令牌。将访问令牌添加到你的Android和iOS项目中。

对于Android,在android/app/src/main/AndroidManifest.xml中添加:

<meta-data
    android:name="com.mapbox.mapboxsdk.android.TelemetryEnabled"
    android:value="true" />
<meta-data
    android:name="com.mapbox.mapboxsdk.android.AccessToken"
    android:value="YOUR_MAPBOX_ACCESS_TOKEN" />  <!-- 替换为你的访问令牌 -->

对于iOS,在ios/Runner/Info.plist中添加:

<key>MGLMapboxAccessToken</key>
<string>YOUR_MAPBOX_ACCESS_TOKEN</string>  <!-- 替换为你的访问令牌 -->

然后,你可以在你的Flutter应用中创建一个展示Mapbox地图的页面。以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Mapbox Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapboxMapPage(),
    );
  }
}

class MapboxMapPage extends StatefulWidget {
  @override
  _MapboxMapPageState createState() => _MapboxMapPageState();
}

class _MapboxMapPageState extends State<MapboxMapPage> {
  final MapboxMapController _controller = MapboxMapController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Mapbox Map'),
      ),
      body: Center(
        child: MapboxMap(
          mapboxMapController: _controller,
          initialCameraPosition: CameraPosition(
            target: LatLng(37.7749, -122.4194), // 旧金山
            zoom: 12.0,
          ),
          styleFromMapbox: MapStyle.streets, // 使用Mapbox的街道风格
          myLocationEnabled: true, // 启用定位
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个显示Mapbox地图的页面。MapboxMap小部件用于渲染地图,并设置了初始的相机位置(旧金山)和地图样式(街道风格)。我们还启用了定位功能,以便用户可以看到他们当前的位置(如果设备上有定位权限并启用)。

请确保你已经替换了YOUR_MAPBOX_ACCESS_TOKEN为你的实际Mapbox访问令牌,并且已经处理了定位权限的请求(如果需要)。

这个示例应该能帮助你快速开始在Flutter应用中使用mapbox_maps_flutter插件展示地图。如果你有更复杂的需求,比如添加标记、绘制多边形等,你可以参考mapbox_maps_flutter的官方文档和示例代码。

回到顶部