> 技术文档 > HarmonyOS NEXT 适配高德地图FlutterSDK实现地图展示,添加覆盖物和移动Camera

HarmonyOS NEXT 适配高德地图FlutterSDK实现地图展示,添加覆盖物和移动Camera


HarmonyOS NEXT 适配高德地图 Flutter SDK 实现地图展示,添加覆盖物和移动 Camera

在现代移动应用开发中,地图功能是许多应用的核心组成部分之一。HarmonyOS NEXT 提供了强大的跨平台开发能力,而高德地图 Flutter SDK 则为开发者提供了丰富的地图功能。因为高德地图FlutterSDK已停止维护,并且也没有鸿蒙测的适配库,所以才有了下面的内容,本文将详细介绍如何在 HarmonyOS NEXT 中适配高德地图 Flutter SDK,实现地图展示、添加覆盖物和移动 Camera 的功能。

一、技术亮点

1.1 Flutter 的优势

  • 高效的构建效率:Flutter 的热重载特性允许开发者即时预览代码更改的影响,极大地提高了开发效率。
  • 跨平台兼容性:Flutter 应用可以在 Android、iOS 和 Web 等多个平台上运行,无需为每个平台单独开发,从而节省了开发成本。
  • 丰富的组件库:Flutter 提供了丰富的组件库,如按钮、文本框、列表等,帮助开发者轻松创建出色的用户界面。

1.2 高德地图 Flutter SDK 的优势

高德地图 Flutter SDK 提供了强大的地图功能,包括地图展示、覆盖物添加和 Camera 操作等。通过与 Flutter 的结合,开发者可以轻松实现地图相关的功能。

二、集成高德地图 SDK

首先我们的基础是要先集成高德地图的FlutterSDK

amap_flutter_map: ^3.0.0

2.1 获取 SDK

首先,你需要在高德开放平台注册并获取 SDK。别忘了申请你的高德SDK的key,具体可以参考高德地图的官网文档

2.2 从ohpm仓库获取高德地图包

 \"dependencies\": { \"@amap/amap_lbs_common\": \">=1.2.0\", \"@amap/amap_lbs_map3d\": \">=2.2.0\"}

2.3 声明权限,工程的oh-package.json5文件中添加依赖

module.json5 中添加必要的权限和模块声明。

{... \"requestPermissions\": [ { \"name\": \'ohos.permission.INTERNET\', }]...

三、地图展示

3.1 接下来进入正题,既然是适配高德FlutterSDK,那肯定需要我们在鸿蒙端做一些重要工作

首先我们需要创建一个AMapView这个类的作用是接收Dart测过来的消息

/** * @FileName : AMapView * @Author : kirk.wang * @Time : 2025/5/7 17:19 * @Description : */import { BinaryMessenger, MethodCall, MethodCallHandler, MethodChannel, MethodResult, StandardMethodCodec } from \"@ohos/flutter_ohos\";import PlatformView, { Params } from \'@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView\'import { common } from \"@kit.AbilityKit\";import { AMapBuilder } from \"./AMapComponent\";export class AMapView extends PlatformView implements MethodCallHandler { methodChannel: MethodChannel; args?: ESObject; constructor(context: common.Context, viewId: number , args: ESObject, message: BinaryMessenger) { super(); this.args = args this.methodChannel = new MethodChannel(message, `amap_flutter_map_${viewId}`, StandardMethodCodec.INSTANCE); this.methodChannel.setMethodCallHandler(this); } onMethodCall(call: MethodCall, result: MethodResult): void { // 接受Dart侧发来的消息 let method: string = call.method; let link1: SubscribedAbstractProperty<number> = AppStorage.link(\'numValue\'); switch (method) { case \'getMessageFromFlutterView\': let value: ESObject = call.args; link1.set(value) console.log(\"nodeController receive message from dart: \"); result.success(true); break; } } getView(): WrappedBuilder<[Params]> { return new WrappedBuilder(AMapBuilder); } public sendMessage = () => { console.log(\"nodeController sendMessage\") //向Dart侧发送消息 this.methodChannel.invokeMethod(\'getMessageFromOhosView\', \'natvie - \'); } dispose(): void { }}

3.2 这个AMapView在什么时候用呢,创建一个AMapPlatformViewFactory类继承自 PlatformViewFactory,用于创建和管理地图相关的原生视图(PlatformView)

import { Any, BinaryMessenger, MessageCodec, PlatformView, PlatformViewFactory } from \"@ohos/flutter_ohos\";import { common } from \"@kit.AbilityKit\";import { AMapView } from \"./AMapView\";export default class AMapPlatformViewFactory extends PlatformViewFactory { message: BinaryMessenger; constructor(message: BinaryMessenger, createArgsCodes: MessageCodec<Object>) { super(createArgsCodes) this.message = message; } public create(context: common.Context, viewId: number, args: Any): PlatformView { return new AMapView(context, viewId, args, this.message); }}

3.3 创建地图插件,注册工厂类

/** * @FileName : AMapFlutterMapPlugin * @Author : kirk.wang * @Time : 2025/5/8 10:15 * @Description : 高德地图插件 */import { Any, BasicMessageChannel, FlutterPlugin, FlutterPluginBinding, MethodChannel, StandardMessageCodec} from \"@ohos/flutter_ohos\";import AMapPlatformViewFactory from \"./AMapPlatformViewFactory\";export default class AMapFlutterMapPlugin implements FlutterPlugin { onDetachedFromEngine(binding: FlutterPluginBinding): void { this.channel?.setMethodCallHandler(null) } private channel?:MethodChannel; private basicChannel?: BasicMessageChannel<Any>; private VIEW_TYPE : string = \"com.amap.flutter.map\"; getUniqueClassName(): string { return \"AMapFlutterMapPlugin\" } onAttachedToEngine(binding: FlutterPluginBinding): void { binding.getPlatformViewRegistry().registerViewFactory(this.VIEW_TYPE, new AMapPlatformViewFactory(binding.getBinaryMessenger(),StandardMessageCodec.INSTANCE)) }}

3.3 创建地图主视图,然后根据传递的数据设置鸿蒙端的高德原生地图,在AMapView里有一个getView,就是返回的下面的视图代码

/** * @FileName : AMapComponent * @Author : kirk.wang * @Time : 2025/5/8 14:20 * @Description : 地图主视图 */import { AMap, BitmapDescriptorFactory, CameraUpdateFactory, LatLng, MapsInitializer, MapView, MapViewComponent, MapViewManager, MarkerOptions} from \'@amap/amap_lbs_map3d\'import { Params } from \'@ohos/flutter_ohos/src/main/ets/plugin/platform/PlatformView\'import { AMapView } from \'./AMapView\'import { ArrayList, HashMap, List } from \'@kit.ArkTS\';import image from \'@ohos.multimedia.image\';import json from \'@ohos.util.json\';const key = \"你在高德地图申请的鸿蒙端的key\";@Componentstruct AMapComponent { @Prop params: Params customView: AMapView = this.params.platformView as AMapView @StorageLink(\'numValue\') storageLink: string = \"first\" @State bkColor: Color = Color.Red aMap?: AMap; aboutToAppear(): void { MapsInitializer.setApiKey(key); MapsInitializer.setDebugMode(true); MapViewManager.getInstance().registerMapViewCreatedCallback((mapview?: MapView, mapViewName?: string) => { if (!mapview) { return; } mapview!.onCreate(); mapview!.getMapAsync((map) => { this.aMap = map; }) }) } build() { Stack() { MapViewComponent({ mapViewName: \"harmony_map_demo\" }).zIndex(0) } .direction(Direction.Ltr) .width(\'100%\') .height(\'100%\') }}@Builderexport function AMapBuilder(params: Params) { AMapComponent({ params: params }) .backgroundColor(Color.Yellow)}

四、添加覆盖物

4.1 根据接收的数据来设置覆盖物,设置地图中心点以及缩放级别

在地图上添加覆盖物时,需要将 Flutter Widget 转换为图片,然后通过原生的 Marker 接口添加到地图上。查看高德Flutter插件可知发送参数的信息,也可以在鸿蒙测断点查看,注意 以下接收数据的key不可更改,否则无法接收到数据,例如:markersToAdd、initialCameraPosition等
Flutter传输的字节数组在鸿蒙端接收有问题,导致这个地方卡了好几天┭┮﹏┭┮

 aboutToAppear(): void { MapsInitializer.setApiKey(key); MapsInitializer.setDebugMode(true); let tempList = this.customView.args?.get(\"markersToAdd\") as List<Map<String, Object>>; let optionsList = new ArrayList<MarkerOptions>() try { tempList.forEach(async (op) => { let options = new MarkerOptions() options.setAlpha(op.get(\'alpha\') as number); let anchor = op.get(\'anchor\') as Array<number> options.setAnchor(anchor[0], anchor[1]); options.setClickable(op.get(\'clickable\') as boolean); options.setDraggable(op.get(\'draggable\') as boolean); options.setInfoWindowEnable(op.get(\'infoWindowEnable\') as boolean); let positionList = op.get(\'position\') as Array<number> if (positionList.length === 2) { options.setPosition(new LatLng(positionList[0], positionList[1])); } options.setZIndex(op.get(\'zIndex\') as number); let icon = op.get(\'icon\') as Array<string | Uint8Array>; if (icon.length >= 2) { try { //因chanel传值导致数据被破坏,无法正确识别Uint8Array参数,所以需要json转换后重新生成Uint8Array //将数据转成JSON字符串 let jsonStr = json.stringify(icon[1]); //将JSON字符串格式化成map let obj = json.parse(jsonStr) as HashMap<string, number>; // 将对象转换为数组 const array = Object.keys(obj).map((key): number => obj[key]); if (Array.isArray(array)) {  //根据最新的数组生成Uint8Array  let icon1 = new Uint8Array(array);  //拷贝字节数组  const buffer1 = icon1.buffer.slice(0);  //通过字节数组生成图片  let imageSource: image.ImageSource = image.createImageSource(buffer1);  let decodingOption: image.DecodingOptions = { editable: true,  }  imageSource.createPixelMap(decodingOption) .then(async (pixelmap: PixelMap) => {  //向options添加图片信息  options.setIcon(BitmapDescriptorFactory.fromPixelMapSync(pixelmap));  //将options添加到数组  optionsList.add(options); }) } } catch (error) { console.error(\'Error:\', error); } } }); } catch (e) { console.log(\"===============Alpha:error:\" + e); } 

4.2 将 Flutter Widget 添加到地图

MapViewManager.getInstance().registerMapViewCreatedCallback((mapview?: MapView, mapViewName?: string) => { if (!mapview) { return; } mapview!.onCreate(); mapview!.getMapAsync((map) => { this.aMap = map; //向地图添加Markers if (optionsList !== null && optionsList.length > 0) { this.aMap?.addMarkers(optionsList); } }) }) }

五、设置地图中心点以及缩放级别

5.1 移动 Camera

通过调用地图的 moveCamera 方法,可以移动地图的 Camera。

 aboutToAppear(): void { ... MapViewManager.getInstance().registerMapViewCreatedCallback((mapview?: MapView, mapViewName?: string) => { if (!mapview) { return; } mapview!.onCreate(); mapview!.getMapAsync((map) => { this.aMap = map; let cameraPosition = this.customView.args?.get(\"initialCameraPosition\") as Map<String, Object>; let targetList = cameraPosition.get(\'target\') as Array<number> //设置地图中心点以及缩放级别 this.aMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(targetList[0], targetList[1]), cameraPosition.get(\'zoom\') as number)); ... }) }) }

至此鸿蒙端的开发工作至此结束

七、Flutter端处理

找到Flutter端的method_channel_amap_flutter_map.dart,里面有个buildView函数,里面有判断只支持

  Widget buildView( Map<String, dynamic> creationParams, Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, void Function(int id) onPlatformViewCreated) { if (defaultTargetPlatform == TargetPlatform.android) { creationParams[\'debugMode\'] = kDebugMode; return AndroidView( viewType: VIEW_TYPE, onPlatformViewCreated: onPlatformViewCreated, gestureRecognizers: gestureRecognizers, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), ); } else if (defaultTargetPlatform == TargetPlatform.iOS) { return UiKitView( viewType: VIEW_TYPE, onPlatformViewCreated: onPlatformViewCreated, gestureRecognizers: gestureRecognizers, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), ); } // else if (defaultTargetPlatform == TargetPlatform.ohos) { // return OhosView( // viewType: VIEW_TYPE, // onPlatformViewCreated: onPlatformViewCreated, // gestureRecognizers: gestureRecognizers, // creationParams: creationParams, // creationParamsCodec: const StandardMessageCodec(), // ); // } return Text(\'当前平台:$defaultTargetPlatform, 不支持使用高德地图插件\'); }

将我注释掉的代码放开即可!接下来就可以在你的鸿蒙设备上调试了。完全按照我的代码,除了高德的key,其余的都不要随便更改哦,否则可能运行出来有问题

六、总结

通过上述步骤,你可以在 HarmonyOS NEXT 中适配高德地图 Flutter SDK,实现地图展示、添加覆盖物和移动 Camera 的功能。Flutter 的跨平台特性和高德地图的强大功能相结合,为开发者提供了极大的便利。

希望本文能够帮助你在 HarmonyOS NEXT 中成功集成高德地图 Flutter SDK,并实现所需的地图功能。如果你在开发过程中遇到任何问题,可以参考高德地图的官方文档,或在相关社区寻求帮助。

七、参考

  • Flutter中的高德地图适配鸿蒙