Fluttify一周一插件 之 高德地图 Flutter插件

5,059 阅读16分钟

Logo

高德地图 地图组件 Flutter插件

GitHub地址:github.com/fluttify-pr…

demo apk下载

依赖

dependencies:
  flutter:
    sdk: flutter
  amap_map_fluttify: ^x.x.x

配置

Android

  1. 注意在app/build.gradleandroid块中配置签名信息, 并在buildTypes块中指定签名信息, 否则将无法匹配到你在高德后台配置的appkey, 例如:
android {
    signingConfigs {
        release {
            keyAlias 'amap_map_test'
            keyPassword 'amap_map_test'
            storeFile file('../amap_map_test.jks')
            storePassword 'amap_map_test'
        }
    }

    buildTypes {
        debug {
            signingConfig signingConfigs.release
        }
        profile {
            signingConfig signingConfigs.release
        }
        release {
            signingConfig signingConfigs.release
        }
    }
}

iOS

  1. 使用地图需要使能UiKitView, 在Info.plist中添加:
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
  1. 定位需要声明权限, 在Info.plist中添加:
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要定位权限</string>
  1. 调用高德地图需要添加白名单:
<key>LSApplicationQueriesSchemes</key>
<array>
	<string>iosamap</string>
	<string>amapuri</string>
</array>

导入

import 'package:amap_map_fluttify/amap_map_fluttify.dart';

使用

显示地图

设置高德Key

在application标签中加入如下内容:

<meta-data android:name="com.amap.api.v2.apikey" android:value="key">
//开发者申请的key  
</meta-data>

点我获取Key
点我查看Key注册时必要数据SHA1和包名的获取方法

初始化地图容器

AmapView是 Widget 类的一个子类, 用于在 Widget树 中放置地图。 AmapView 是地图容器。用 AmapView 加载地图的方法与 提供的其他 Widget 一样,具体的使用步骤如下:

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

class CreateMapScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('自定义地图')),
      body: AmapView(),
    );
  }
}

插件已经在app生命周期回调中处理了相关回调。

显示地图

AmapController 类是地图的控制器类,用来操作地图。它所承载的工作包括:地图图层切换(如卫星图、黑夜地图)、改变地图状态(地图旋转角度、俯仰角、中心点坐标和缩放级别)、添加点标记(Marker)、绘制几何图形(Polyline、Polygon、Circle)、各类事件监听(点击、手势等)等,AmapController 是地图 SDK 最重要的核心类,诸多操作都依赖它完成。

AmapView 对象初始化完毕之后,构造 AmapController 对象。示例代码如下:

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

class CreateMapScreen extends StatefulWidget {
  @override
  _CreateMapScreenState createState() => _CreateMapScreenState();
}

class _CreateMapScreenState extends State<CreateMapScreen> {
  AmapController _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('自定义地图')),
      body: AmapView(
        onMapCreated: (controller) async {
          _controller = controller;
        },
      ),
    );
  }
}

运行您刚完成的工程就可以在您的 APP 中看到高德地图了。运行后的效果如下图所示(左Android,右iOS): 显示地图_Android 显示地图_iOS

显示定位蓝点

定位蓝点指的是进入地图后显示当前位置点的功能。自Android 3D地图 SDK 5.0.0版本之后定位蓝点实现无需依赖 Android 定位 SDK ,5.0.0 版本之前需要引入地图 SDK 和定位 SDK 到工程中。效果如下: 定位蓝点

构造定位参数

final option = MyLocationOption(
  show: true, // 是否显示
  myLocationType: MyLocationType.Locate, //定位模式
  interval: Duration.zero, // 定位间隔
  strokeColor: Colors.transparent, // 精度圈边框颜色
  strokeWidth: 0, //精度圈边框宽度
  fillColor: Colors.transparent, // 精度圈填充色
  iconProvider: AssetImage('图片路径'), // 自定义定位图标
  anchorU: 0.0, // 锚点u
  anchorV: 0.0, // 锚点v
);

定位蓝点展现模式

enum MyLocationType {
  /// 只定位
  Show,
  /// 定位一次, 并移动到中心
  Locate,
  /// 跟随
  Follow,
  /// 方向跟随
  Rotate,
}

获取经纬度信息

通过

class AmapController {
  Future<LatLng> getLocation({
    Duration interval = const Duration(milliseconds: 500),
    Duration timeout = const Duration(seconds: 10),
  });
}

方法获取经纬度信息,建议拿到位置之后调用逆地理编码接口获取。

显示室内地图

Android 3D地图 SDK 集成了室内地图功能,支持室内外地图一体化展示。

功能介绍

开启室内地图后,如果可见区域内包含室内地图覆盖区域(如:凯德Mall等知名商场),且缩放达到一定级别,便可直接在地图上看到精细室内地图效果。
缩放级别≥17级时,地图上可以显示室内地图。
缩放级别≥18级时,不仅可以看到室内地图效果,还允许操作切换楼层,显示精细化室内地图。
如下图示:
室内地图1 室内地图2

开启室内地图方法

3D 地图 SDK中默认会关闭室内地图显示,如有需要可使用类AmapController中的 showIndoorMap(bool enable) 自行开启。

await controller.showIndoorMap(bool enable); //true:显示室内地图;false:不显示;

切换地图图层

地图插件提供了几种预置的地图图层,包括卫星图、白昼地图(即最常见的黄白色地图)、夜景地图、导航地图、路况图层。
插件提供图层类型枚举,详细如下: 注意:路况图层是通过开关控制,不通过常量控制。

名称说明
MapType.Standard标准视图
MapType.Satellite卫星视图
MapType.Night黑夜视图
MapType.Navi导航视图
MapType.Bus公交视图

卫星地图

卫星地图在显示卫片(卫星照片)的同时也可以显示路网信息,设置卫星地图的代码及显示效果如下:

await controller.setMapType(MapType.Satellite);

显示效果如下:
satellite android satellite ios

夜景地图

设置夜景地图的代码如下:

await controller.setMapType(MapType.Night);

显示效果如下:
night android night ios

路况图层

路况图依据实时路况数据渲染,与前两种设置方式不太相同,路况图实现的方法如下:

await controller.showTraffic(true);

显示效果如下:
showTraffic android showTraffic ios

使用离线地图

高德3D 地图 SDK支持离线地图功能。(2D 地图 SDK 不支持离线地图功能)
离线地图可满足在无网络环境下查看地图信息的需求,在设备本地有离线地图数据的情况下,SDK 会优先加载离线地图。

离线地图UI组件

离线地图UI组件涵盖城市下载、暂停、更新、删除以及关键字城市查询等功能,是高德地图客户端离线地图功能的一个子集,UI交互风格上靠拢高德地图app,也考虑到与开发者应用UI的融合问题,尽可能的保持了简约极致。以下方法实现一键完成离线地图开发:

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

class OfflineManagerScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('打开离线地图管理')),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // 打开离线地图UI组件
            AmapService.instance.openOfflineMapManager();
          },
          child: Text('离线地图管理'),
        ),
      ),
    );
  }
}

UI示意:
offline android offline ios

显示英文地图

地图支持切换中英文显示。具体实现代码:

await controller.setMapLanguage(Language.English);

显示效果如下:
english android english ios

自定义地图

高德地图支持使用可视化自定义地图模版改变底图颜色和样式,实现可视化的编辑和控制显示地图元素。

创建地图样式

高德地图开放平台的开发者在取得开发者账号后,可以进入开发者控制台,在地图自定义平台选择“创建地图样式”,可以选择一个模板进行创建。
图片

编辑地图样式

在创建的页面的左侧列表选择任一要素编辑样式属性;也可以单击地图,在弹出的列表中选择要素进行编辑。 图片

发布地图样式并下载

编辑完成后点击右上角“保存”->“发布”,发布完成后,选择“使用方法”,然后选择“android”平台,点击“下载离线文件”。 图片 图片

设定样式文件

一、设定离线样式文件
1、在官网控制台-我的地图样式中选择与当前使用的地图SDK版本号所对应的版本进行样式文件下载:
图片

2.下载得到的Zip文件,内部目录结构如下,每个文件都会对应 setCustomMapStyle 中一个参数:

文件名称文件内容说明对应参数
style_extra.data扩展内容,如网格背景色等styleExtraPath
style.data具体样式配置styleDataPath
textures.zip纹理图片(zip文件)texturePath
import 'package:amap_map_fluttify/amap_map_fluttify.dart';
import 'package:flutter/material.dart';

class CreateMapScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: AmapView(
        zoomLevel: 10,
        onMapCreated: (controller) async {
          // styleDataPath等参数路径与使用Image.asset('path')时同理;
          await controller.setCustomMapStyle(styleDataPath: 'raw/style.data', styleExtraPath: 'raw/style_extra.data');
        },
      ),
    );
  }
}

显示效果如下:
custom android custom ios

控件交互

控件是指浮在地图图面上的一系列用于操作地图的组件,例如缩放按钮、指南针、定位按钮、比例尺等。

缩放按钮

缩放按钮是提供给 App 端用户控制地图缩放级别的交换按钮,每次点击改变1个级别,此控件默认打开,可以通过以下方法控制其隐藏:
图片

await controller.showZoomControl(true);

指南针

指南针用于向 App 端用户展示地图方向,默认不显示。通过如下接口控制其显示:
图片

await controller.showCompass(true);

定位按钮

App 端用户可以通过点击定位按钮在地图上标注一个蓝色定位点,代表其当前位置。不同于以上控件,定位按钮内部的逻辑实现依赖定位 SDK。
SDK 没有提供自定义定位按钮的功能,如果您想要实现该功能,可以浏览参考自定义定位按钮的内容。
图片

await controller.showLocateControl(true);

比例尺控件

比例尺控件(最大比例是1:10m,最小比例是1:1000Km),位于地图右下角,可控制其显示与隐藏,设置的方法是:
图片

await controller.showScaleControl(true);

手势交互

地图 SDK 提供了多种手势供 App 端用户与地图之间进行交互,如缩放、旋转、滑动、倾斜。这些手势默认开启,如果想要关闭某些手势,可以通过 AmapController 类提供的接口来控制手势的开关。

手势方法说明

以下是控制手势生效与否的方法:

名称调用方法
缩放手势AmapController.setZoomGesturesEnabled(bool)
滑动手势AmapController.setScrollGesturesEnabled(bool)
旋转手势AmapController.setRotateGesturesEnabled(bool)
倾斜手势AmapController.setTiltGesturesEnabled(bool)
所有手势AmapController.setAllGesturesEnabled(bool)

缩放手势

缩放手势可改变地图的缩放级别,地图响应的手势如下:

  • 双击地图可以使缩放级别增加1 (放大)
  • 两个手指捏/拉伸 也可以禁用或启用缩放手势。禁用缩放手势不会影响用户使用地图上的缩放控制按钮。以下是控制缩放手势开启关闭的代码:
await controller.setZoomGesturesEnabled(true);

滑动手势

您可以用手指拖动地图四处滚动(平移)或用手指滑动地图(动画效果),也可以禁用或开启平移(滑动)手势。
以下介绍控制缩放手势开启关闭的方法,示例代码如下:

await controller.isScrollGesturesEnabled(true);

旋转手势

您可以用两个手指在地图上转动,可以旋转3D矢量地图,也可以禁用旋转手势。
以下介绍控制旋转手势开启关闭的方法,示例代码如下:

await controller.setRotateGesturesEnabled(true);

倾斜手势

用户可以在地图上放置两个手指,移动它们一起向下或向上去增加或减小倾斜角,也可以禁用倾斜手势。
以下是控制倾斜手势开启关闭的代码:

await controller.setTiltGesturesEnabled(true);

开启以中心点进行手势操作的方法:

await controller.setZoomByCenter(true);

调用方法交互

方法交互的概念是从程序角度出发提出的。地图 SDK 提供了很多与地图交互的接口方法,例如:改变地图显示的区域(即改变地图中心点)、改变地图的缩放级别、限制地图的显示范围等。
地图视角交互的核心方法均依赖 AmapController 类提供的方法。

改变地图的中心点

如果想改变地图中心点,可以通过如下方法,animated参数可以控制是否以动画方式移动地图:

await controller.setCenterCoordinate(LatLng(23.16, 113.23), animated: false);

改变地图的缩放级别

如果想改变地图的缩放级别,可以通过如下方法,animated参数可以控制是否以动画方式移动地图:

await controller.setZoomLevel(10, animated: false);

限制地图的显示范围

手机屏幕仅显示设定的地图范围,例如:希望设置仅显示北京市区地图,可使用此功能。注意:如果限制了地图显示范围,地图旋转手势将会失效。

final southWest = LatLng(40, 116);
final northEast = LatLng(42, 118);
await controller.setMapRegionLimits(southWest, northEast);

地图截屏功能

地图 SDK 支持对当前屏幕显示区域进行截屏,可以对地图、覆盖物(包含信息窗口)、Logo进行截取屏幕,这其中不包括地图控件、Toast窗口。
详细示例如下:

final Uint8List data = await controller.screenShot();

返回对象为图片数据,使用Image.memory(:Uint8List)可以直接显示。

绘制点标记

点标记用来在地图上标记任何位置,例如用户位置、车辆位置、店铺位置等一切带有位置属性的事物。
地图 SDK 提供的点标记功能包含两大部分,一部分是点(俗称 Marker)、另一部分是浮于点上方的信息窗体(俗称 InfoWindow)。同时,SDK 对 Marker 和 InfoWindow 封装了大量的触发事件,例如点击事件、长按事件、拖拽事件。
Marker 有默认风格,同时也支持自定义。由于内容丰富,以下只能展示一些基础功能的使用,详细内容可分别参考手册。
Marker
MarkerOption

绘制默认 Marker

绘制 Marker 的代码如下:

LatLng latLng = LatLng(39.906901,116.397972);
final Marker marker = controller.addMarker(MarkerOption(coordinate: latLng));

以上代码绘制的 Marker 效果如下图:
english android english ios

Marker 常用属性

名称说明
position在地图上标记位置的经纬度值。必填参数
title点标记的标题
snippet点标记的内容
draggable点标记是否可拖拽
visible点标记是否可见
anchorU,anchorV点标记的锚点
alpha点的透明度

绘制自定义 Marker

可根据实际的业务需求,在地图指定的位置上添加自定义的 Marker。MarkerOptions 是设置 Marker 参数变量的类,自定义 Marker 时会经常用到。
下面以自定义 Marker 图标为例进行代码说明:

final marker = await controller.addMarker(
  MarkerOption(
    coordinate: LatLng(39.906901,116.397972),
    iconProvider: AssetImage('images/test_icon.png'),
  ),
);

以上代码绘制的 Marker 效果如下图:
english android english ios

绘制动画效果 Marker

插件 提供了给 Marker 设置动画的方法,具体实现方法如下:

final marker = await controller.addMarker(
  MarkerOption(
    coordinate: getNextLatLng(),
    iconProvider: AssetImage('images/test_icon.png'),
    anchorU: 0.5,
    anchorV: 1,
    visible: false,
  ),
);
await marker.startAnimation(ScaleMarkerAnimation(
  fromValue: 0.8,
  toValue: 1.2,
  duration: Duration(milliseconds: 1000),
  repeatCount: 0,
));
await marker.setVisible(true);

由于动画显示过程是异步的,如果直接添加marker并马上执行动画的话,可能会出现marker先被添加到地图上,然后一闪而过开始执行动画。这里提供的是一个变通方法,可以先添加一个隐藏的marker,等动画开始调用之后,再显示marker。 english android english ios

可触发的 Marker 事件

Marker 点击事件

点击 Marker 时会回调AmapController::setMarkerClickedListener,监听器的实现示例如下:

await controller.setMarkerClickedListener((marker) async {
  // 点击marker后替换图片
  marker.setIcon(
    AssetImage('images/test_icon.png'),
    createLocalImageConfiguration(context),
  );
});

Marker 拖拽事件 拖拽 Marker 时会回调AmapController::setMarkerDragListener(:onMarkerDragStart:onMarkerDragging:onMarkerDragEnd),监听器的实现示例如下:

await controller.setMarkerDragListener(
  onMarkerDragStart: (marker) async {
    // 拖拽开始
  },
  onMarkerDragging: (marker) async {
    // 拖拽中
  },
  onMarkerDragEnd: (marker) async {
    // 拖拽结束
  },
);

绘制 InfoWindow

InfoWindow 是点标记的一部分,默认的 Infowindow 只显示 Marker 对象的两个属性,一个是 title 和另一个 snippet,如果希望对InfoWindow 的样式或者内容有自定义需求,可以参考如下内容。

绘制默认 Infowindow

SDK 为用户提供了默认的 InfoWindow 样式,调用 Marker 类的 showInfoWindow()hideInfoWindow() 方法可以控制显示和隐藏。当改变 Markertitlesnippet 属性时,再次调用 showInfoWindow(),可以更新 InfoWindow 显示内容。效果如下:
english android english ios

绘制自定义 InfoWindow

自定义InfoWindow需要在AmapController上调用showCustomInfoWindow,需要传入Marker对象和自定义InfoWindow的widget。注意这里的widget仅作为截图提供给原生端显示,如果widget中有按钮之类的动态内容,则转成静态截图后都是无效的。实现示例如下:

await controller.setMarkerClickedListener((marker) async {
  await controller.showCustomInfoWindow(
    marker,
    Card(
      elevation: 10,
      child: Container(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.location_on),
            Text(await marker.title),
          ],
        ),
      ),
    ),
  );
});

english android english ios

可触发的 InfoWindow 事件

点击 InfoWindow 时会回调AmapController.setInfoWindowClickListener(:onInfoWindowClicked),监听器的实现示例如下:

await controller.setInfoWindowClickListener((marker) async {
  toast('${await marker.title}, ${await marker.coordinate}');
});

绘制折线

地图上绘制的线是由 Polyline 类定义实现的,线由一组经纬度(LatLng对象)点连接而成。

绘制一条线

与点标记一样,Polyline 的属性操作集中在 PolylineOption 类中,添加一条线的示例如下:

await controller.addPolyline(PolylineOption(
  coordinateList: [
    LatLng(39.999391, 116.135972),
    LatLng(39.898323, 116.057694),
    LatLng(39.900430, 116.265061),
    LatLng(39.955192, 116.140092),
  ],
  strokeColor: Colors.red,
  width: 10,
));

上面的代码定义该折线的颜色为红色,宽度为 10 像素,效果如下图:
english android english ios

绘制线常用参数

名称说明
coordinateList折线经纬度列表
width折线宽度
strokeColor折线颜色
textureProvider自定义纹理
lineCapType线段末端样式
lineJoinType线段连接处样式
dashType是否虚线

绘制面

地图上的面分为圆形和多边形两种。

绘制圆

圆形由 Circle 类定义实现,构造一个圆形需要确定它的圆心和半径,具体的示例代码如下:

await controller.addCircle(CircleOption(
  center: LatLng(39.999391, 116.135972),
  radius: 10000,
  width: 10,
  strokeColor: Colors.green,
));

上面的代码定义该圆形的边线颜色为绿色,宽度10 像素,效果如下图:
offline android offline ios

绘制多边形

多边形是由 Polygon 类定义的一组在地图上的封闭线段组成的图形,它由一组 LatLng 点按照传入顺序连接而成的封闭图形。与绘制线类似,面的属性操作集中在 PolygonOption 中。

final polygon = await controller.addPolygon(PolygonOption(
  coordinateList: [
    LatLng(39.999391, 116.135972),
    LatLng(39.898323, 116.057694),
    LatLng(39.900430, 116.265061),
    LatLng(39.955192, 116.140092),
  ],
  width: 10,
  strokeColor: Colors.green,
));

上面的代码定义该圆形的边线颜色为绿色,宽度10 像素,效果如下图:
offline android offline ios

绘制热力图

热力图功能提供将业务数据展示在地图上,可以给使用者直观描述一个区域的人员,车辆等事物的热度情况。

组织热力图数据

以下以本地模拟数据为例,简单说明 SDK 热力图需要的是经纬度点数组/列表数据。

示例代码如下:

await controller.addHeatmapTileOverlay(
  HeatmapTileOption(
    coordinateList: getNextBatchLatLng(50),
    gradient: RadialGradient(
      colors: [Colors.blue, Colors.yellow, Colors.red],
      stops: <double>[0.08, 0.4, 1.0],
    ),
  ),
);

效果图如下:
offline android offline ios

点平滑移动

功能说明:根据输入的关键点和时间参数,实现点的平滑移动效果。
使用场景:可应用到展示车辆行驶轨迹、用户移动轨迹等场景。
效果示例:
english android english ios

如何实现点平滑移动

实现平滑移动的配置参数都在SmoothMoveMarkerOption类内,其中path是移动路径的经纬度列表,iconProvider为marker图片,duration为动画时长。

await controller.addSmoothMoveMarker(
  SmoothMoveMarkerOption(
    path: [for (int i = 0; i < 10; i++) getNextLatLng()],
    iconProvider: AssetImage('images/test_icon.png'),
    duration: Duration(seconds: 10),
  ),
);

绘制海量点图层

应用于移动端的海量点图层用于批量展现具有相似属性的坐标点数据。海量点图层支持处理的点数量级跨度较大,从几十个点至十万个点(建议不超过100000个点数据)都可以应用海量点图层进行处理。

如下图所示的处理上万个点的海量点图层效果:
图

展示海量点

添加海量点与添加普通Marker类似,只需要构造对应类型参数即可。
示例代码:

final overlay = await controller.addMultiPointOverlay(
  MultiPointOption(
    pointList: [
      for (int i = 0; i < 10000; i++)
        PointOption(
          coordinate: getNextLatLng(),
          id: i.toString(),
          title: 'Point$i',
          snippet: 'Snippet$i',
          object: 'Object$i',
        )
    ],
    iconProvider: AssetImage('images/test_icon.png'),
  ),
);

在返回的overlay对象上调用remove方法,即可删除对应海量点图层。

海量点点击事件

await controller.setMultiPointClickedListener(
  (id, title, snippet, object) async {
    toast(
      'id: $id, title: $title, snippet: $snippet, object: $object',
    );
  },
);

LICENSE

Copyright (C) 2020 yohom

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see www.gnu.org/licenses/.