part of amap_flutter_map; typedef void MapCreatedCallback(AMapController controller); ///用于展示高德地图的Widget class AMapWidget extends StatefulWidget { ///高德开放平台的key /// final AMapApiKey? apiKey; /// 初始化时的地图中心点 final CameraPosition initialCameraPosition; ///地图类型 final MapType mapType; ///自定义地图样式 final CustomStyleOptions? customStyleOptions; ///定位小蓝点 final MyLocationStyleOptions? myLocationStyleOptions; ///缩放级别范围 final MinMaxZoomPreference? minMaxZoomPreference; ///地图显示范围 final LatLngBounds? limitBounds; ///显示路况开关 final bool trafficEnabled; /// 地图poi是否允许点击 final bool touchPoiEnabled; ///是否显示3D建筑物 final bool buildingsEnabled; ///是否显示底图文字标注 final bool labelsEnabled; ///是否显示指南针 final bool compassEnabled; ///是否显示比例尺 final bool scaleEnabled; ///是否支持缩放手势 final bool zoomGesturesEnabled; ///是否支持滑动手势 final bool scrollGesturesEnabled; ///是否支持旋转手势 final bool rotateGesturesEnabled; ///是否支持倾斜手势 final bool tiltGesturesEnabled; /// 地图上显示的Marker final Set<Marker> markers; /// 地图上显示的polyline final Set<Polyline> polylines; /// 地图上显示的polygon final Set<Polygon> polygons; /// 地图创建成功的回调, 收到此回调之后才可以操作地图 final MapCreatedCallback? onMapCreated; /// 相机视角持续移动的回调 final ArgumentCallback<CameraPosition>? onCameraMove; /// 相机视角移动结束的回调 final ArgumentCallback<CameraPosition>? onCameraMoveEnd; /// POI搜索的回调 final ArgumentCallback<PoiResult>? onMapSearchPOI; /// inputTip搜索的回调 final ArgumentCallback<InputTipsResult>? onMapSearchInputTips; /// regeocode搜索的回调 final ArgumentCallback<RegeocodeResult>? onMapSearchRegeocode; /// 地图单击事件的回调 final ArgumentCallback<LatLng>? onTap; /// 地图长按事件的回调 final ArgumentCallback<LatLng>? onLongPress; /// 地图POI的点击回调,需要`touchPoiEnabled`true,才能回调 final ArgumentCallback<AMapPoi>? onPoiTouched; ///位置回调 final ArgumentCallback<AMapLocation>? onLocationChanged; ///需要应用到地图上的手势集合 final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers; ///高德合规声明配置 /// /// 高德SDK合规使用方案请参考:https://lbs.amap.com/news/sdkhgsy final AMapPrivacyStatement ?privacyStatement; /// 创建一个展示高德地图的widget /// /// 如果使用的高德地图SDK的版本是8.1.0及以上版本, /// 在app首次启动时必须传入高德合规声明配置[privacyStatement],后续如果没有变化不需要重复设置 /// <li>[privacyStatement.hasContains] 隐私权政策是否包含高德开平隐私权政策</li> /// <li>[privacyStatement.hasShow] 是否已经弹窗展示给用户</li> /// <li>[privacyStatement.hasAgree] 隐私权政策是否已经取得用户同意</li> /// 以上三个值,任何一个为false都会造成地图插件不工作(白屏情况) /// /// 高德SDK合规使用方案请参考:https://lbs.amap.com/news/sdkhgsy /// /// [AssertionError] will be thrown if [initialCameraPosition] is null; const AMapWidget({ Key? key, this.privacyStatement, this.apiKey, this.initialCameraPosition = const CameraPosition(target: LatLng(39.909187, 116.397451), zoom: 10), this.mapType = MapType.normal, this.buildingsEnabled = true, this.compassEnabled = false, this.labelsEnabled = true, this.limitBounds, this.minMaxZoomPreference, this.rotateGesturesEnabled = true, this.scaleEnabled = true, this.scrollGesturesEnabled = true, this.tiltGesturesEnabled = true, this.touchPoiEnabled = true, this.trafficEnabled = false, this.zoomGesturesEnabled = true, this.onMapCreated, this.gestureRecognizers = const <Factory<OneSequenceGestureRecognizer>>{}, this.customStyleOptions, this.myLocationStyleOptions, this.onCameraMove, this.onCameraMoveEnd, this.onMapSearchPOI, this.onMapSearchInputTips, this.onMapSearchRegeocode, this.onLocationChanged, this.onTap, this.onLongPress, this.onPoiTouched, this.markers = const <Marker>{}, this.polylines = const <Polyline>{}, this.polygons = const <Polygon>{}, }) : super(key: key); /// @override State<StatefulWidget> createState() => _MapState(); } class _MapState extends State<AMapWidget> { Map<String, Marker> _markers = <String, Marker>{}; Map<String, Polyline> _polylines = <String, Polyline>{}; Map<String, Polygon> _polygons = <String, Polygon>{}; final Completer<AMapController> _controller = Completer<AMapController>(); late _AMapOptions _mapOptions; @override Widget build(BuildContext context) { AMapUtil.init(context); final Map<String, dynamic> creationParams = <String, dynamic>{ 'privacyStatement': widget.privacyStatement?.toMap(), 'apiKey': widget.apiKey?.toMap(), 'initialCameraPosition': widget.initialCameraPosition.toMap(), 'options': _mapOptions.toMap(), 'markersToAdd': serializeOverlaySet(widget.markers), 'polylinesToAdd': serializeOverlaySet(widget.polylines), 'polygonsToAdd': serializeOverlaySet(widget.polygons), }; Widget mapView = _methodChannel.buildView( creationParams, widget.gestureRecognizers, onPlatformViewCreated, ); return mapView; } @override void initState() { super.initState(); _mapOptions = _AMapOptions.fromWidget(widget); _markers = keyByMarkerId(widget.markers); _polygons = keyByPolygonId(widget.polygons); _polylines = keyByPolylineId(widget.polylines); print('initState AMapWidget'); } @override void dispose() async { super.dispose(); AMapController controller = await _controller.future; controller.disponse(); print('dispose AMapWidget with mapId: ${controller.mapId}'); } @override void reassemble() { super.reassemble(); print('reassemble AMapWidget'); } @override void deactivate() async { super.deactivate(); print('deactivate AMapWidget}'); } @override void didUpdateWidget(covariant AMapWidget oldWidget) { super.didUpdateWidget(oldWidget); _updateOptions(); _updateMarkers(); _updatePolylines(); _updatePolygons(); } Future<void> onPlatformViewCreated(int id) async { final AMapController controller = await AMapController.init( id, widget.initialCameraPosition, this, ); _controller.complete(controller); final MapCreatedCallback? _onMapCreated = widget.onMapCreated; if (_onMapCreated != null) { _onMapCreated(controller); } } void onMarkerTap(String markerId) { final Marker? _marker = _markers[markerId]; if (_marker != null) { final ArgumentCallback<String>? _onTap = _marker.onTap; if (_onTap != null) { _onTap(markerId); } } } void onMarkerDragEnd(String markerId, LatLng position) { final Marker? _marker = _markers[markerId]; if (_marker != null) { final MarkerDragEndCallback? _onDragEnd = _marker.onDragEnd; if (_onDragEnd != null) { _onDragEnd(markerId, position); } } } void onPolylineTap(String polylineId) { final Polyline? _polyline = _polylines[polylineId]; if (_polyline != null) { final ArgumentCallback<String>? _onTap = _polyline.onTap; if (_onTap != null) { _onTap(polylineId); } } } void _updateOptions() async { final _AMapOptions newOptions = _AMapOptions.fromWidget(widget); final Map<String, dynamic> updates = _mapOptions._updatesMap(newOptions); if (updates.isEmpty) { return; } final AMapController controller = await _controller.future; // ignore: unawaited_futures controller._updateMapOptions(updates); _mapOptions = newOptions; } void _updateMarkers() async { final AMapController controller = await _controller.future; // ignore: unawaited_futures controller._updateMarkers(MarkerUpdates.from(_markers.values.toSet(), widget.markers)); _markers = keyByMarkerId(widget.markers); } void _updatePolylines() async { final AMapController controller = await _controller.future; controller._updatePolylines(PolylineUpdates.from(_polylines.values.toSet(), widget.polylines)); _polylines = keyByPolylineId(widget.polylines); } void _updatePolygons() async { final AMapController controller = await _controller.future; controller._updatePolygons(PolygonUpdates.from(_polygons.values.toSet(), widget.polygons)); _polygons = keyByPolygonId(widget.polygons); } } //高德地图参数设置 class _AMapOptions { ///地图类型 final MapType mapType; ///自定义地图样式 final CustomStyleOptions? customStyleOptions; ///定位小蓝点 final MyLocationStyleOptions? myLocationStyleOptions; //缩放级别范围 final MinMaxZoomPreference? minMaxZoomPreference; ///地图显示范围 final LatLngBounds? limitBounds; ///显示路况开关 final bool? trafficEnabled; /// 地图poi是否允许点击 final bool? touchPoiEnabled; ///是否显示3D建筑物 final bool? buildingsEnabled; ///是否显示底图文字标注 final bool? labelsEnabled; ///是否显示指南针 final bool? compassEnabled; ///是否显示比例尺 final bool? scaleEnabled; ///是否支持缩放手势 final bool? zoomGesturesEnabled; ///是否支持滑动手势 final bool? scrollGesturesEnabled; ///是否支持旋转手势 final bool? rotateGesturesEnabled; ///是否支持仰角手势 final bool? tiltGesturesEnabled; _AMapOptions({ this.mapType = MapType.normal, this.buildingsEnabled, this.customStyleOptions, this.myLocationStyleOptions, this.compassEnabled, this.labelsEnabled, this.limitBounds, this.minMaxZoomPreference, this.scaleEnabled, this.touchPoiEnabled, this.trafficEnabled, this.rotateGesturesEnabled, this.scrollGesturesEnabled, this.tiltGesturesEnabled, this.zoomGesturesEnabled, }); static _AMapOptions fromWidget(AMapWidget map) { return _AMapOptions( mapType: map.mapType, buildingsEnabled: map.buildingsEnabled, compassEnabled: map.compassEnabled, labelsEnabled: map.labelsEnabled, limitBounds: map.limitBounds, minMaxZoomPreference: map.minMaxZoomPreference, scaleEnabled: map.scaleEnabled, touchPoiEnabled: map.touchPoiEnabled, trafficEnabled: map.trafficEnabled, rotateGesturesEnabled: map.rotateGesturesEnabled, scrollGesturesEnabled: map.scrollGesturesEnabled, tiltGesturesEnabled: map.tiltGesturesEnabled, zoomGesturesEnabled: map.zoomGesturesEnabled, customStyleOptions: map.customStyleOptions?.clone(), myLocationStyleOptions: map.myLocationStyleOptions?.clone(), ); } Map<String, dynamic> toMap() { final Map<String, dynamic> optionsMap = <String, dynamic>{}; void addIfNonNull(String fieldName, dynamic value) { if (value != null) { optionsMap[fieldName] = value; } } addIfNonNull('mapType', mapType.index); addIfNonNull('buildingsEnabled', buildingsEnabled); addIfNonNull('customStyleOptions', customStyleOptions?.clone().toMap()); addIfNonNull('compassEnabled', compassEnabled); addIfNonNull('labelsEnabled', labelsEnabled); addIfNonNull('limitBounds', limitBounds?.toJson()); addIfNonNull('minMaxZoomPreference', minMaxZoomPreference?.toJson()); addIfNonNull('scaleEnabled', scaleEnabled); addIfNonNull('touchPoiEnabled', touchPoiEnabled); addIfNonNull('trafficEnabled', trafficEnabled); addIfNonNull('rotateGesturesEnabled', rotateGesturesEnabled); addIfNonNull('scrollGesturesEnabled', scrollGesturesEnabled); addIfNonNull('tiltGesturesEnabled', tiltGesturesEnabled); addIfNonNull('zoomGesturesEnabled', zoomGesturesEnabled); addIfNonNull('myLocationStyle', myLocationStyleOptions?.clone().toMap()); return optionsMap; } Map<String, dynamic> _updatesMap(_AMapOptions newOptions) { final Map<String, dynamic> prevOptionsMap = toMap(); return newOptions.toMap() ..removeWhere((String key, dynamic value) => (_checkChange(key, prevOptionsMap[key], value))); } bool _checkChange(String key, dynamic preValue, dynamic newValue) { if (key == 'myLocationStyle' || key == 'customStyleOptions') { return preValue?.toString() == newValue?.toString(); } else { return preValue == newValue; } } }