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 = argsthis.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";@Component
struct AMapComponent {@Prop params: ParamscustomView: AMapView = this.params.platformView as AMapView@StorageLink('numValue') storageLink: string = "first"@State bkColor: Color = Color.RedaMap?: 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%')}
}@Builder
export 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字符串格式化成maplet obj = json.parse(jsonStr) as HashMap<string, number>;// 将对象转换为数组const array = Object.keys(obj).map((key): number => obj[key]);if (Array.isArray(array)) {//根据最新的数组生成Uint8Arraylet 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;//向地图添加Markersif (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函数,里面有判断只支持
@overrideWidget 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中的高德地图适配鸿蒙
相关文章:
HarmonyOS NEXT 适配高德地图FlutterSDK实现地图展示,添加覆盖物和移动Camera
HarmonyOS NEXT 适配高德地图 Flutter SDK 实现地图展示,添加覆盖物和移动 Camera 在现代移动应用开发中,地图功能是许多应用的核心组成部分之一。HarmonyOS NEXT 提供了强大的跨平台开发能力,而高德地图 Flutter SDK 则为开发者提供了丰富的…...

DeepSeek执行流程加速指南:跨框架转换与编译优化的核心策略全解析
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
docker-compose——安装redis
文章目录 一、编写docker-compose.yaml文件二、编写redis.conf文件三、启动docker-compose 一、编写docker-compose.yaml文件 version: 3.3 services:redis:image: redis:latestcontainer_name: redisrestart: alwaysports:- 6379:6379volumes:- ./redis/data:/data- ./redis/…...

manuskript开源程序是面向作家的开源工具
一、软件介绍 文末提供程序和源码下载 manuskript开源程序是面向作家的开源工具,Manuskript 可在 GNU/Linux、Mac OS X 和 Windows 上运行。 二、Features 特征 Manuskript provides a rich environment to help writers create their first draft and then furt…...

游戏引擎学习第281天:在房间之间为摄像机添加动画效果
回顾并为今天的内容定下基调 这次我们要继续深入处理实体系统。在前一阶段对实体系统做了一些很酷的改动,但现在到了要认真面对和完善它的时候。 今天的主要目标是修复并优化摄像机在房间之间移动时的逻辑。在上一次的实现中,我们重新启用了基于房间的…...

Kaamel隐私合规洞察:Temu在韩被罚事件分析
Kaamel隐私合规与数据安全团队分析报告 韩国个人信息保护委员会(PIPC)对中国电子商务巨头Temu处以巨额罚款,原因是其严重违反了用户数据保护法律 。核心违规行为包括未经适当披露或用户同意非法跨境传输数据、未能指定当地代表、账户注销流程…...

计算机视觉----基于锚点的车道线检测、从Line-CNN到CLRNet到CLRKDNet 本文所提算法Line-CNN 后续会更新以下全部算法
本文所提算法如下: 叙述按时间顺序 你也可以把本文当作快速阅读这几篇文献的一个途径 所有重要的部分我都已经标注并弄懂其原理 方便自己也是方便大家 Line-CNN:基于线提议单元的端到端交通线检测 摘要 交通线检测是一项基础且具有挑战性的任务。以往的…...

25.5.15
没有比水题更令人开心的事情了 典型的并查集题目,并查集分为并和查,并就是把有关系的父亲根结点设为同一个,查就是在成功构造后对其进行查询 查通过递归实现 if (x f[x])return x; return f[x] find(f[x]); 由于并查集的特点࿰…...

5.重建大师数据管理模块介绍
摘要:本文主要介绍重建大师数据管理模块,包含:照片、点云数据可视化管理工具。 数据管理界面主要包含工具栏、可视化界面和照片组列表三部分。 图 数据管理界面 1.工具栏 工具栏包含以下功能按钮,包含添加照片、视频、点云、控制…...

MATLAB安装常见问题及解决方案详解(含代码示例)
MATLAB作为科学计算和工程分析的核心工具,其安装过程可能因操作系统版本、硬件配置或网络环境等因素而出现各种问题。本文基于MATLAB官方文档和社区经验,系统总结了安装过程中常见的问题,并提供详细的解决方案和代码示例,帮助用户…...

微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)
一、系统介绍 本智能商城系统是基于当今主流技术栈开发的一款多端商城解决方案,主要包括微信小程序前端、SpringBoot 后端服务以及 Vue 管理后台三大部分。系统融合了线上商城的核心功能,支持商品浏览、下单、支付、订单管理等操作,适用于中小…...

【Spark分析HBase数据】Spark读取并分析HBase数据
Spark读取并分析HBase数据 一、摘要二、实现过程三、小结 一、摘要 Apache Spark 是一个快速、通用的大数据处理引擎,提供了丰富的 API 用于数据处理和分析。HBase 是一个分布式、可扩展的 NoSQL 数据库,适合存储海量结构化和半结构化数据。Spark 与 HB…...
大数据Flink相关面试题(一)
文章目录 一、基础概念1. Flink的核心设计目标是什么?与Spark Streaming的架构差异?2. 解释Flink的“有状态流处理”概念。3. Flink的流处理(DataStream API)与批处理(DataSet API)底层执行模型有何不同&…...
填坑记: 古董项目Apache POI 依赖异常排除
当你看到NoSuchMethodError的时候,不要慌,深呼吸,这可能只是JAR包版本的问题… 引子:一个平静的周二下午 那是一个看似平常的周二下午,系统运行良好,开发团队在有条不紊地推进着新功能的开发。突然&#x…...

leetcode2934. 最大化数组末位元素的最少操作次数-medium
1 题目:最大化数组末位元素的最少操作次数 官方标定难度:中 给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,这两个数组的长度都是 n 。 你可以执行一系列 操作(可能不执行)。 在每次操作中,你可以选…...

环境配置与MySQL简介
目录 1 环境配置 2 MySQL简介 1 环境配置 本专栏使用CentOS7进行讲解。首先我们查看系统中是否已经安装了MySQL,可以使用rpm -qa 命令查看系统安装包/压缩包 列表 这只是看我们是否下载过对应安装包,不一定就安装了。如果我们需要重新下载,…...
07_SpringBoot2集成Redis连接失败
🌟 07_SpringBoot2 集成 Redis 连接失败 ❓ 场景描述 在 Spring Boot 2 项目中集成 Redis 时,将配置写成了如下形式: spring:data:redis:host: localhostport: 6379password: 123456结果启动项目时 Redis 连接失败,报错内容类似…...
mysql的一个缺点
最近再移植一个从oracle转mysql的项目,喜提一个报错: You cant specify target table A016 for update in FROM clause 对应的程序代码: public void setCurrent(String setId, String pk, String userId) throws SysException {String[]…...

适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
一、软件介绍 文末提供程序和源码下载 该项目利用 Ultralytics 最先进的 YOLO11 模型将您的 iOS 设备转变为用于对象检测的强大实时推理工具。直接从 App Store 下载该应用程序,或浏览我们的指南,将 YOLO 功能集成到您自己的 Swift 应用程序中。 二、…...

Java零基础学习Day12——集合ArrayList
一、基本使用 1. 集合与数组 集合只存引用数据类型;长度可变 数组可存基本数据类型、引用数据类型;长度固定 2. 基本格式 ArrayList<String> list new ArrayList<>(); 3. 方法 增、删 import java.util.ArrayList; public class St…...

[论文阅读]Formalizing and Benchmarking Prompt Injection Attacks and Defenses
Formalizing and Benchmarking Prompt Injection Attacks and Defenses Formalizing and Benchmarking Prompt Injection Attacks and Defenses | USENIX 33rd USENIX Security Symposium (USENIX Security 24) 提出了一个框架来形式化提示注入攻击,对提示注入攻击…...
ffmpeg 写入avpacket时候,即av_interleaved_write_frame方法是如何不需要 业务层释放avpacket的 逻辑分析
我们在通过 av_interleaved_write_frame方法 写入 avpacket的时候,通常不需要关心 avpacket的生命周期。 本文分析一下内部实现的部分。 ----> 代表一个内部实现。 A(){ B(); C(); } B(){ D(); } 表示为: A ---->B(); ---->D(); ---->C(); int…...
目标检测中的IoU损失函数
目标检测中的IoU损失函数 目标检测中的IoU损失函数一、为什么需要IoU损失函数?二、常见IoU损失函数详解1. **IoU Loss**2. **GIoU Loss(Generalized IoU)**3. **DIoU Loss(Distance IoU)**4. **CIoU Loss(C…...
深入剖析 MyBatis 位运算查询:从原理到最佳实践
深入剖析 MyBatis 位运算查询:从原理到最佳实践 引言 在数据库设计中,位运算是一种高效存储和查询多选字段的常用技术。然而,在实际开发中,特别是在使用 MyBatis 这样的 ORM 框架时,位运算查询往往会遇到一些意想不到…...

JavaScript性能优化实战,从理论到落地的全面指南
在前端开发领域,JavaScript的性能优化是提升用户体验的核心环节。随着Web应用复杂度的提升,开发者面临的性能瓶颈也日益多样化。本文将从理论分析、代码实践和工具使用三个维度,系统性地讲解JavaScript性能优化的实战技巧,并通过大…...
第二个五年计划!
下一阶段!5年后!33岁!体重维持在125斤内!腰围74! 健康目标: 体检指标正常,结节保持较小甚至变小! 工作目标: 每年至少在一次考评里拿A(最高S,A我理…...
【行为型之中介者模式】游戏开发实战——Unity复杂系统协调与通信架构的核心秘诀
文章目录 🕊️ 中介者模式(Mediator Pattern)深度解析一、模式本质与核心价值二、经典UML结构三、Unity实战代码(成就系统协调)1. 定义中介者接口与同事基类2. 实现具体同事类3. 实现具体中介者4. 客户端使用 四、模式…...
分布式微服务系统架构第125集:AI大模型
加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ 一、user 表(用户表) sql 复制编辑 create table if not exists user (id bigint auto_increment comment id pri…...

MySQL 8.0 OCP 英文题库解析(三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题16~25 试题16:…...
MapReduce 模型
引言 MapReduce 是分布式计算领域的里程碑式模型,由 Google 在 2004 年论文中首次提出,旨在简化海量数据处理的复杂性。其核心思想是通过函数式编程的 Map (映射)和 Reduce (归约)阶段&#x…...