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中的高德地圖適配鴻蒙