Flutter地图展示插件arcgis_maps的使用

发布于 1周前 作者 caililin 来自 Flutter

Flutter地图展示插件arcgis_maps的使用

使用ArcGIS Maps SDK for Flutter构建原生移动应用,可以集成诸如2D数据可视化和编辑、地理编码和路由等功能,并部署到iOS或Android平台。

平台支持

  • 使用macOS开发主机来部署到iOS和Android移动设备。
  • 使用Windows开发主机来部署到Android移动设备。

有关更多信息,请参阅我们的详细系统需求

功能

ArcGIS Maps SDK for Flutter的第一个版本不包括其他ArcGIS Maps SDKs for Native Apps的所有API和功能。这些缺失的功能将在后续版本中引入。更多详细信息,请查看我们的API对等页面

开始使用

在您的应用程序中添加arcgis_maps作为依赖项后,运行以下命令以下载并安装arcgis_maps_core

dart run arcgis_maps install

注意:在Windows上,这一步骤需要创建符号链接的权限。您可以以管理员身份运行此步骤,或者转到“设置 > 更新与安全 > 开发者选项”,并打开“开发者模式”。

然后将arcgis_maps_core添加到您的.gitignore文件中:

# 杂项
arcgis_maps_core
...

更多信息,请参阅我们的安装和设置指南

如果您是Flutter开发的新手或初次接触ArcGIS,请查看我们的详细入门指南

平台特定配置

Android

通过编辑android/app/build.gradle文件来更新最低要求:

  • 设置Android NDK 25.2.9519653为最低版本
  • 设置Android SDK 26为最低版本
android {
    ...
    ndkVersion "25.2.9519653"
    ...
    defaultConfig {
        ...
        minSdk 26
        ...
    }
...
}

通过编辑android/settings.gradle文件来更新Kotlin版本:

  • 将Kotlin版本设置为1.9.0
plugins {
    ...
    id "org.jetbrains.kotlin.android" version "1.9.0" apply false
}

iOS

通过编辑ios/Podfile文件来设置iOS 16.0为最低版本。首先取消注释该行,然后更新版本号。

platform :ios, '16.0'

通过添加Runtimecorearcgis_maps_ffi Pod到Runner目标部分来配置arcgis_maps_core

target 'Runner' do
  ...

  pod 'Runtimecore', :podspec => '../arcgis_maps_core/ios/Runtimecore.podspec'
  pod 'arcgis_maps_ffi', :podspec => '../arcgis_maps_core/ios/arcgis_maps_ffi.podspec'

  ...
end

使用pod update来配置Pods:

cd ios && pod update && cd ..

额外权限

一些ArcGIS Maps SDK for Flutter的功能在部署应用程序时需要额外的密钥和权限。有关更多信息,请参阅我们的详细安装和设置指南

使用API

在小部件树中添加一个ArcGISMapView,分配一个ArcGISMapViewController并应用一个ArcGISMap

...
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: ArcGISMapView(
        controllerProvider: () => ArcGISMapView.createController()
          ..arcGISMap = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISImagery),
      ),
    );
  }
...

有关信息和可用类列表,请参阅我们的API参考

我们还提供了示例代码,演示了ArcGIS Maps SDK for Flutter的功能以及如何在您的应用程序中使用它们,以及教程,提供逐步指导以构建包含ArcGIS Maps功能的应用程序。

API密钥认证

上述示例代码使用了可以通过API密钥访问的ArcGIS Location Platform底图。要获取访问令牌,请参阅我们的入门指南

如果您已经有访问令牌,请将其设置在应用程序的main()方法中:

void main() {
    ArcGISEnvironment.apiKey = 'YOUR_ACCESS_TOKEN';
    ...
}

其他资源


示例代码

//
// COPYRIGHT © 2023 Esri
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// This material is licensed for use under the Esri Master
// Agreement (MA) and is bound by the terms and conditions
// of that agreement.
//
// You may redistribute and use this code without modification,
// provided you adhere to the terms and conditions of the MA
// and include this copyright notice.
//
// See use restrictions at http://www.esri.com/legal/pdfs/mla_e204_e300/english
//
// For additional information, contact:
// Environmental Systems Research Institute, Inc.
// Attn: Contracts and Legal Department
// 380 New York Street
// Redlands, California 92373
// USA
//
// email: legal@esri.com
//

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

void main() async {
  // 通过命令行参数 --dart-define-from-file 提供您的apiKey。
  const apiKey = String.fromEnvironment('API_KEY');
  // 或者替换上面的行并在这里硬编码您的apiKey:
  // const apiKey = ''; // 您的API Key在这里。
  if (apiKey.isEmpty) {
    throw Exception('apiKey undefined');
  } else {
    ArcGISEnvironment.apiKey = apiKey;
  }

  // ArcGISMapsExample 是此示例应用程序的主要小部件。
  runApp(const MaterialApp(home: ArcGISMapsExample()));
}

/// ArcGISMapsExample 演示如何使用 arcgis_maps 构建基本的2D映射应用程序。
/// 此应用程序:
/// - 创建并显示了一个 [ArcGISMapView] 小部件及其 [ArcGISMapViewController]。
/// - 设置了一个 [ArcGISMap] 到 [ArcGISMapViewController.arcGISMap]。
/// - 向 [ArcGISMap.operationalLayers] 添加了一组 [FeatureLayer]。
/// - 向 [ArcGISMapViewController.graphicsOverlays] 添加了一个 [GraphicsOverlay]。
/// - 配置了一个 [GeometryEditor] 以创建新的 [Graphic] 并添加到 [GraphicsOverlay.graphics]。
class ArcGISMapsExample extends StatefulWidget {
  const ArcGISMapsExample({super.key});

  [@override](/user/override)
  State<ArcGISMapsExample> createState() => _ArcGISMapsExampleState();
}

class _ArcGISMapsExampleState extends State<ArcGISMapsExample> {
  // 创建一个用于地图视图的控制器。
  final _mapViewController = ArcGISMapView.createController();
  // 创建一个具有底图样式的地图。
  final _map = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISImageryStandard);
  // 创建一个用于显示图形的图形覆盖层。
  final _graphicsOverlay = GraphicsOverlay();
  // 创建一个几何编辑器以创建新几何图形。
  final _geometryEditor = GeometryEditor();

  // 一个标志,指示UI是否准备好使用。
  var _ready = false;
  // 一个标志,基于编辑是否正在进行来控制UI。
  var _editingInProgress = false;
  // 一个标志,基于图层可见性来控制UI。
  var _layersVisible = true;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        top: false,
        child: Stack(
          children: [
            Column(
              children: [
                Expanded(
                  // 在小部件树中添加一个 ArcGISMapView 小部件,并设置一个控制器。
                  child: ArcGISMapView(
                    controllerProvider: () => _mapViewController,
                    // 配置当地图视图准备就绪时的操作。
                    onMapViewReady: onMapViewReady,
                    // 配置用户点击地图视图小部件时的操作。
                    onTap: !_editingInProgress ? onTap : null,
                  ),
                ),
              ],
            ),
            // 添加按钮到UI以切换图层可见性和创建新图形。
            Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Padding(
                  padding: const EdgeInsets.only(bottom: 30.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: [
                      Tooltip(
                        message: '切换图层可见性',
                        child: ElevatedButton(
                          onPressed: _ready ? toggleLayerVisibility : null,
                          child: Icon(
                            _layersVisible
                                ? Icons.visibility
                                : Icons.visibility_off,
                          ),
                        ),
                      ),
                      Tooltip(
                        message: '创建一个新的图形',
                        child: ElevatedButton(
                          onPressed: _ready
                              ? _editingInProgress
                                  ? saveEdits
                                  : startEditing
                              : null,
                          child: Icon(
                            _editingInProgress ? Icons.save : Icons.draw,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  void onMapViewReady() async {
    // 使用 ArcGISOnline 门户和所需项目ID和图层数创建特征层。
    final arcGISOnlinePortal = Portal.arcGISOnline();
    final buildings = FeatureLayer.withFeatureLayerItem(
      PortalItem.withPortalAndItemId(
        portal: arcGISOnlinePortal,
        itemId: '0ec8512ad21e4bb987d7e848d14e7e24',
      ),
    );

    final primaryRoads = FeatureLayer.withItem(
      item: PortalItem.withPortalAndItemId(
        portal: arcGISOnlinePortal,
        itemId: 'f42ecc08a3634182b8678514af35fac3',
      ),
      layerId: 2,
    );

    final secondaryRoads = FeatureLayer.withItem(
      item: PortalItem.withPortalAndItemId(
        portal: arcGISOnlinePortal,
        itemId: 'f42ecc08a3634182b8678514af35fac3',
      ),
      layerId: 6,
    );

    final localRoads = FeatureLayer.withItem(
      item: PortalItem.withPortalAndItemId(
        portal: arcGISOnlinePortal,
        itemId: 'f42ecc08a3634182b8678514af35fac3',
      ),
      layerId: 8,
    );

    // 将图层添加到地图的操作层。
    _map.operationalLayers
        .addAll([buildings, primaryRoads, secondaryRoads, localRoads]);

    // 定义一个渲染器以样式化任何添加到图形覆盖层中的图形。
    _graphicsOverlay.renderer = SimpleRenderer(
      symbol: SimpleFillSymbol(
        style: SimpleFillSymbolStyle.solid,
        color: Colors.cyanAccent.withOpacity(0.4),
        outline: SimpleLineSymbol(
          style: SimpleLineSymbolStyle.dash,
          color: Colors.cyan,
          width: 2,
        ),
      ),
    );

    // 添加一些初始图形以在启动时显示。
    _graphicsOverlay.graphics.addAll(createGraphics());
    // 将图形覆盖层添加到地图视图控制器以显示它。
    _mapViewController.graphicsOverlays.add(_graphicsOverlay);

    // 分配地图到地图视图控制器。
    _mapViewController.arcGISMap = _map;

    // 设置一个初始视点以在启动时显示。
    _mapViewController.setViewpoint(
      Viewpoint.fromCenter(
        ArcGISPoint(
          x: -13310927.924,
          y: 4367840.399,
        ),
        scale: 5000,
      ),
    );

    // 分配几何编辑器到地图视图控制器。
    _mapViewController.geometryEditor = _geometryEditor;

    // 配置一些几何编辑器设置。
    await configureGeometryEditor();

    // 完成所有配置后启用UI。
    setState(() => _ready = true);
  }

  void onTap(Offset offset) async {
    // 在地图视图小部件上的点击屏幕点执行识别操作。
    final identifyGraphicsOverlayResult =
        await _mapViewController.identifyGraphicsOverlay(
      _graphicsOverlay,
      screenPoint: offset,
      tolerance: 22.0,
    );

    // 如果在点击位置识别到图形,则显示对话框。
    if (identifyGraphicsOverlayResult.graphics.isNotEmpty) {
      final identifiedGraphic = identifyGraphicsOverlayResult.graphics.first;
      if (mounted) {
        showDialog(
          context: context,
          builder: (context) {
            return AlertDialog(
              alignment: Alignment.center,
              title: const Text('点击了图形'),
              content: Text(
                '中心点:\n'
                'x: ${identifiedGraphic.geometry?.extent.center.x}\n'
                'y: ${identifiedGraphic.geometry?.extent.center.y}',
              ),
              actions: [
                TextButton(
                  onPressed: () => Navigator.of(context).pop(),
                  child: const Text('确定'),
                ),
              ],
            );
          },
        );
      }
    }
  }

  // 创建一个初始图形列表以显示。
  List<Graphic> createGraphics() {
    const largePolygonJson = '''
        {"rings":[[[-13311062.662636876,4368080.2952499595],[-13311057.127783449,4368076.3178643389],
            [-13311061.634682227,4367769.666227323],[-13311103.149254397,4367767.9142679712],
            [-13311104.043995325,4367698.8852444943],[-13311091.295635514,4367692.5083970781],
            [-13311089.376387239,4367666.5662346007],[-13311068.132243464,4367666.3481658408],
            [-13311067.365961147,4367656.5642296085],[-13311058.564161845,4367655.1021004347],
            [-13311060.260100655,4367569.1472102059],[-13311314.397355149,4367569.6304349722],
            [-13311329.11803535,4367581.7673969641],[-13311325.279430484,4368078.8186992826],
            [-13311229.822984351,4368080.2299739141],[-13311228.715676585,4367907.4899566751],
            [-13311146.872602904,4367905.7258633124],[-13311145.543586506,4368081.7248991122],
            [-13311062.662636876,4368080.2952499595]]],
            "spatialReference":{"latestWkid":3857,"wkid":102100}}''';
    // 使用提供的JSON字符串创建多边形几何。
    final largePolygonGeometry = Geometry.fromJsonString(largePolygonJson);
    // 使用几何创建一个新的图形。
    final largeGraphic = Graphic(geometry: largePolygonGeometry);

    const mediumPolygonJson = '''
        {"rings":[[[-13310930.109359179,4367955.6946535213],[-13310939.951467492,4367565.1394895678],
            [-13311052.146218125,4367566.2247806583],[-13311050.523812089,4367957.6713874312],
            [-13310930.109359179,4367955.6946535213]]],
            "spatialReference":{"latestWkid":3857,"wkid":102100}}''';
    // 使用提供的JSON字符串创建多边形几何。
    final mediumPolygonGeometry = Geometry.fromJsonString(mediumPolygonJson);
    // 使用几何创建一个新的图形。
    final mediumGraphic = Graphic(geometry: mediumPolygonGeometry);

    const smallPolygonJson = '''
        {"rings":[[[-13311036.736801982,4368106.8208614551],[-13311035.197346671,4368240.6001734752],
            [-13310765.222839184,4368239.1766897719],[-13310763.479211008,4368109.3070116714],
            [-13311036.736801982,4368106.8208614551]]],
            "spatialReference":{"latestWkid":3857,"wkid":102100}}''';
    // 使用提供的JSON字符串创建多边形几何。
    final smallPolygonGeometry = Geometry.fromJsonString(smallPolygonJson);
    // 使用几何创建一个新的图形。
    final smallGraphic = Graphic(geometry: smallPolygonGeometry);

    return [largeGraphic, mediumGraphic, smallGraphic];
  }

  // 配置一些几何编辑器设置。
  Future<void> configureGeometryEditor() async {
    // 使用光标顶点工具。
    _geometryEditor.tool = ReticleVertexTool();

    // 设置地图的特征分块模式。
    // 为了确保数据完整性,编辑时需要全分辨率的捕捉。
    _map.loadSettings.featureTilingMode =
        FeatureTilingMode.enabledWithFullResolutionWhenSupported;
    // 确保地图和每个图层按顺序加载以同步捕捉设置。
    await _map.load();
    await Future.wait(_map.operationalLayers.map((layer) => layer.load()));

    // 启用几何编辑器的捕捉。
    _geometryEditor.snapSettings.isEnabled = true;
    _geometryEditor.snapSettings.isGeometryGuidesEnabled = true;

    // 将图形覆盖层和特征层添加到捕捉源,并为每个源启用捕捉。
    _geometryEditor.snapSettings.syncSourceSettings();
    for (final snapSourceSetting
        in _geometryEditor.snapSettings.sourceSettings) {
      snapSourceSetting.isEnabled = true;
    }
  }

  // 开始几何编辑器以创建新几何图形。
  void startEditing() {
    // 使用多边形几何类型开始几何编辑器以创建新的多边形几何图形。
    _geometryEditor.startWithGeometryType(GeometryType.polygon);
    setState(() => _editingInProgress = true);
  }

  // 保存由几何编辑器创建的几何图形到图形覆盖层。
  void saveEdits() {
    // 停止几何编辑器并获取返回的几何图形。
    final geometry = _geometryEditor.stop();
    setState(() => _editingInProgress = false);

    if (geometry != null) {
      // 如果有有效的几何图形,使用它创建一个新的图形。
      final graphic = Graphic(geometry: geometry);
      // 将图形添加到图形覆盖层。
      _graphicsOverlay.graphics.add(graphic);
    }
  }

  // 切换地图的操作层的可见性。
  void toggleLayerVisibility() {
    setState(() => _layersVisible = !_layersVisible);

    if (_mapViewController.arcGISMap != null) {
      // 获取地图的操作层列表中的每个图层并切换其 isVisible 属性。
      for (final layer in _mapViewController.arcGISMap!.operationalLayers) {
        layer.isVisible = _layersVisible;
      }
    }
  }
}

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

1 回复

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


当然,以下是一个使用 arcgis_maps 插件在 Flutter 中展示地图的基本示例。这个插件允许你在 Flutter 应用中嵌入和展示 ArcGIS 地图。

首先,确保你已经在 pubspec.yaml 文件中添加了 arcgis_maps 依赖:

dependencies:
  flutter:
    sdk: flutter
  arcgis_maps: ^latest_version  # 请替换为最新的版本号

然后运行 flutter pub get 来获取依赖。

接下来,在你的 Flutter 项目中,你可以创建一个简单的页面来展示 ArcGIS 地图。以下是一个完整的示例代码:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ArcGIS Map Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ArcGISMapScreen(),
    );
  }
}

class ArcGISMapScreen extends StatefulWidget {
  @override
  _ArcGISMapScreenState createState() => _ArcGISMapScreenState();
}

class _ArcGISMapScreenState extends State<ArcGISMapScreen> {
  // ArcGIS MapView Controller
  late ArcGISMapViewController _mapViewController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ArcGIS Map Example'),
      ),
      body: ArcGISMapView(
        // Provide your ArcGIS API key here
        apiKey: 'YOUR_ARCGIS_API_KEY',
        onMapViewInitialized: (ArcGISMapViewController controller) {
          _mapViewController = controller;
          // Optionally, set the initial map viewpoint
          _setViewpoint();
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Example: Zoom to a specific location
          _zoomToLocation();
        },
        tooltip: 'Zoom to Location',
        child: Icon(Icons.zoom_in),
      ),
    );
  }

  void _setViewpoint() {
    // Define the initial viewpoint (e.g., centered on a specific location)
    final Viewpoint centerAt = Viewpoint(
      targetGeometry: Envelope(
        xmin: -122.45,
        ymin: 37.75,
        xmax: -122.4,
        ymax: 37.8,
        spatialReference: SpatialReference(wkid: 4326), // WGS 84
      ),
      scale: 50000, // Define the scale
    );

    _mapViewController.setViewpoint(centerAt);
  }

  void _zoomToLocation() {
    // Define a new viewpoint to zoom to
    final Viewpoint newViewpoint = Viewpoint(
      targetGeometry: Point(
        x: -122.4324,
        y: 37.7749,
        spatialReference: SpatialReference(wkid: 4326), // WGS 84
      ),
      scale: 20000, // Define the new scale
    );

    _mapViewController.setViewpoint(newViewpoint);
  }
}

注意事项:

  1. API Key: 你需要在 ArcGISMapViewapiKey 属性中提供有效的 ArcGIS API 密钥。你可以通过注册 ArcGIS 开发者账户并创建一个新的应用来获取这个密钥。

  2. 依赖版本: 确保你使用的是 arcgis_maps 插件的最新版本。你可以在 pub.dev 上查找最新的版本信息。

  3. 地图初始化: 在 onMapViewInitialized 回调中,你可以获取 ArcGISMapViewController 实例,用于控制地图视图(例如设置视点和缩放级别)。

  4. 空间参考系: 在定义地理位置时,确保使用正确的空间参考系(例如 WGS 84,其 wkid 为 4326)。

这个示例展示了如何使用 arcgis_maps 插件在 Flutter 应用中嵌入和展示 ArcGIS 地图,并包含了一些基本的交互操作,如设置初始视点和缩放到特定位置。你可以根据具体需求进一步扩展和自定义地图功能。

回到顶部