25 changed files with 2608 additions and 5 deletions
@ -0,0 +1,174 @@ |
|||||
|
part of amap_service_map; |
||||
|
|
||||
|
final MethodChannelAMapFlutterMap _methodChannel = |
||||
|
AMapFlutterPlatform.instance as MethodChannelAMapFlutterMap; |
||||
|
|
||||
|
/// 地图通信中心 |
||||
|
class AMapController { |
||||
|
final int mapId; |
||||
|
final _MapState _mapState; |
||||
|
|
||||
|
AMapController._( |
||||
|
CameraPosition initCameraPosition, |
||||
|
this._mapState, { |
||||
|
required this.mapId, |
||||
|
}) { |
||||
|
_connectStreams(mapId); |
||||
|
} |
||||
|
|
||||
|
///根据传入的id初始化[AMapController] |
||||
|
/// 主要用于在[AMapWidget]初始化时在[AMapWidget.onMapCreated]中初始化controller |
||||
|
static Future<AMapController> init( |
||||
|
int id, |
||||
|
CameraPosition initialCameration, |
||||
|
_MapState mapState, |
||||
|
) async { |
||||
|
await _methodChannel.init(id); |
||||
|
return AMapController._(initialCameration, mapState, mapId: id); |
||||
|
} |
||||
|
|
||||
|
///只用于测试 |
||||
|
///用于与native的通信 |
||||
|
@visibleForTesting |
||||
|
MethodChannel get channel { |
||||
|
return _methodChannel.channel(mapId); |
||||
|
} |
||||
|
|
||||
|
void _connectStreams(int mapId) { |
||||
|
if (_mapState.widget.onLocationChanged != null) { |
||||
|
_methodChannel |
||||
|
.onLocationChanged(mapId: mapId) |
||||
|
.listen( |
||||
|
(LocationChangedEvent e) => |
||||
|
_mapState.widget.onLocationChanged!(e.value), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (_mapState.widget.onCameraMove != null) { |
||||
|
_methodChannel |
||||
|
.onCameraMove(mapId: mapId) |
||||
|
.listen( |
||||
|
(CameraPositionMoveEvent e) => |
||||
|
_mapState.widget.onCameraMove!(e.value), |
||||
|
); |
||||
|
} |
||||
|
if (_mapState.widget.onCameraMoveEnd != null) { |
||||
|
_methodChannel |
||||
|
.onCameraMoveEnd(mapId: mapId) |
||||
|
.listen( |
||||
|
(CameraPositionMoveEndEvent e) => |
||||
|
_mapState.widget.onCameraMoveEnd!(e.value), |
||||
|
); |
||||
|
} |
||||
|
if (_mapState.widget.onTap != null) { |
||||
|
_methodChannel |
||||
|
.onMapTap(mapId: mapId) |
||||
|
.listen(((MapTapEvent e) => _mapState.widget.onTap!(e.value))); |
||||
|
} |
||||
|
if (_mapState.widget.onLongPress != null) { |
||||
|
_methodChannel |
||||
|
.onMapLongPress(mapId: mapId) |
||||
|
.listen( |
||||
|
((MapLongPressEvent e) => _mapState.widget.onLongPress!(e.value)), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (_mapState.widget.onPoiTouched != null) { |
||||
|
_methodChannel |
||||
|
.onPoiTouched(mapId: mapId) |
||||
|
.listen( |
||||
|
((MapPoiTouchEvent e) => _mapState.widget.onPoiTouched!(e.value)), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
_methodChannel |
||||
|
.onMarkerTap(mapId: mapId) |
||||
|
.listen((MarkerTapEvent e) => _mapState.onMarkerTap(e.value)); |
||||
|
|
||||
|
_methodChannel |
||||
|
.onMarkerDragEnd(mapId: mapId) |
||||
|
.listen( |
||||
|
(MarkerDragEndEvent e) => |
||||
|
_mapState.onMarkerDragEnd(e.value, e.position), |
||||
|
); |
||||
|
|
||||
|
_methodChannel |
||||
|
.onPolylineTap(mapId: mapId) |
||||
|
.listen((PolylineTapEvent e) => _mapState.onPolylineTap(e.value)); |
||||
|
} |
||||
|
|
||||
|
void disponse() { |
||||
|
_methodChannel.dispose(id: mapId); |
||||
|
} |
||||
|
|
||||
|
Future<void> _updateMapOptions(Map<String, dynamic> optionsUpdate) { |
||||
|
return _methodChannel.updateMapOptions(optionsUpdate, mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
Future<void> _updateMarkers(MarkerUpdates markerUpdates) { |
||||
|
return _methodChannel.updateMarkers(markerUpdates, mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
Future<void> _updatePolylines(PolylineUpdates polylineUpdates) { |
||||
|
return _methodChannel.updatePolylines(polylineUpdates, mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
Future<void> _updatePolygons(PolygonUpdates polygonUpdates) { |
||||
|
return _methodChannel.updatePolygons(polygonUpdates, mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
///改变地图视角 |
||||
|
/// |
||||
|
///通过[CameraUpdate]对象设置新的中心点、缩放比例、放大缩小、显示区域等内容 |
||||
|
/// |
||||
|
///(注意:iOS端设置显示区域时,不支持duration参数,动画时长使用iOS地图默认值350毫秒) |
||||
|
/// |
||||
|
///可选属性[animated]用于控制是否执行动画移动 |
||||
|
/// |
||||
|
///可选属性[duration]用于控制执行动画的时长,默认250毫秒,单位:毫秒 |
||||
|
Future<void> moveCamera( |
||||
|
CameraUpdate cameraUpdate, { |
||||
|
bool animated = true, |
||||
|
int duration = 250, |
||||
|
}) { |
||||
|
return _methodChannel.moveCamera( |
||||
|
cameraUpdate, |
||||
|
mapId: mapId, |
||||
|
animated: animated, |
||||
|
duration: duration, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
///设置地图每秒渲染的帧数 |
||||
|
Future<void> setRenderFps(int fps) { |
||||
|
return _methodChannel.setRenderFps(fps, mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
///地图截屏 |
||||
|
Future<Uint8List?> takeSnapshot() { |
||||
|
return _methodChannel.takeSnapshot(mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
/// 获取地图审图号(普通地图) |
||||
|
/// |
||||
|
/// 任何使用高德地图API调用地图服务的应用必须在其应用中对外透出审图号 |
||||
|
/// |
||||
|
/// 如高德地图在"关于"中体现 |
||||
|
Future<String?> getMapContentApprovalNumber() { |
||||
|
return _methodChannel.getMapContentApprovalNumber(mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
/// 获取地图审图号(卫星地图) |
||||
|
/// |
||||
|
/// 任何使用高德地图API调用地图服务的应用必须在其应用中对外透出审图号 |
||||
|
/// |
||||
|
/// 如高德地图在"关于"中体现 |
||||
|
Future<String?> getSatelliteImageApprovalNumber() { |
||||
|
return _methodChannel.getSatelliteImageApprovalNumber(mapId: mapId); |
||||
|
} |
||||
|
|
||||
|
/// 清空缓存 |
||||
|
Future<void> clearDisk() { |
||||
|
return _methodChannel.clearDisk(mapId: mapId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
/// 高德地图Flutter插件入口文件 |
||||
|
library amap_service_map; |
||||
|
|
||||
|
import 'dart:async'; |
||||
|
import 'dart:typed_data'; |
||||
|
|
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'package:amap_flutter_map/src/core/amap_flutter_platform.dart'; |
||||
|
import 'package:amap_flutter_map/src/core/map_event.dart'; |
||||
|
import 'package:amap_flutter_map/src/core/method_channel_amap_flutter_map.dart'; |
||||
|
import 'package:amap_flutter_map/src/types/types.dart'; |
||||
|
import 'package:flutter/foundation.dart'; |
||||
|
import 'package:flutter/gestures.dart'; |
||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:flutter/services.dart'; |
||||
|
|
||||
|
export 'package:amap_flutter_map/src/types/types.dart'; |
||||
|
|
||||
|
part 'amap_controller.dart'; |
||||
|
part 'amap_widget.dart'; |
||||
@ -0,0 +1,425 @@ |
|||||
|
part of amap_service_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; |
||||
|
|
||||
|
/// 地图单击事件的回调 |
||||
|
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.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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
import 'package:amap_flutter_map/src/core/method_channel_amap_flutter_map.dart'; |
||||
|
import 'package:flutter/cupertino.dart'; |
||||
|
import 'package:flutter/foundation.dart'; |
||||
|
import 'package:flutter/gestures.dart'; |
||||
|
import 'package:flutter/services.dart'; |
||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart'; |
||||
|
|
||||
|
/// “amap_flutter_map”平台特定实现必须扩展的接口 |
||||
|
abstract class AMapFlutterPlatform extends PlatformInterface { |
||||
|
static final Object _token = Object(); |
||||
|
AMapFlutterPlatform() : super(token: _token); |
||||
|
static AMapFlutterPlatform _instance = MethodChannelAMapFlutterMap() as AMapFlutterPlatform; |
||||
|
|
||||
|
/// The default instance of [AMapFlutterPlatform] to use. |
||||
|
/// |
||||
|
/// Defaults to [MethodChannelAMapFlutterMap]. |
||||
|
static AMapFlutterPlatform get instance => _instance; |
||||
|
|
||||
|
static set instance(AMapFlutterPlatform instance) { |
||||
|
PlatformInterface.verifyToken(instance, _token); |
||||
|
_instance = instance; |
||||
|
} |
||||
|
|
||||
|
/// /// Initializes the platform interface with [id]. |
||||
|
/// |
||||
|
/// This method is called when the plugin is first initialized. |
||||
|
Future<void> init(int id) { |
||||
|
throw UnimplementedError('init() has not been implemented.'); |
||||
|
} |
||||
|
|
||||
|
void dispose({required int id}) { |
||||
|
throw UnimplementedError('dispose() has not been implemented.'); |
||||
|
} |
||||
|
|
||||
|
Widget buildView( |
||||
|
Map<String, dynamic> creationParams, |
||||
|
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, |
||||
|
PlatformViewCreatedCallback onPlatformViewCreated) { |
||||
|
throw UnimplementedError('buildView() has not been implemented.'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'package:amap_flutter_map/amap_flutter_map.dart'; |
||||
|
|
||||
|
///地图事件处理 |
||||
|
class MapEvent<T> { |
||||
|
/// 地图id |
||||
|
final int mapId; |
||||
|
|
||||
|
///返回的内容,对应的[MethodCall]中的[[arguments]] |
||||
|
final T value; |
||||
|
|
||||
|
/// 构造一个event |
||||
|
/// |
||||
|
/// `mapId` 当前地图的id |
||||
|
/// `value` 需要传输的值,可以为`null`. |
||||
|
MapEvent(this.mapId, this.value); |
||||
|
} |
||||
|
|
||||
|
///定位回调接口 |
||||
|
class LocationChangedEvent extends MapEvent<AMapLocation> { |
||||
|
LocationChangedEvent(int mapId, AMapLocation value) : super(mapId, value); |
||||
|
} |
||||
|
|
||||
|
///地图移动回调 |
||||
|
class CameraPositionMoveEvent extends MapEvent<CameraPosition> { |
||||
|
CameraPositionMoveEvent(int mapId, CameraPosition value) |
||||
|
: super(mapId, value); |
||||
|
} |
||||
|
|
||||
|
///地图移动结束回调 |
||||
|
class CameraPositionMoveEndEvent extends MapEvent<CameraPosition> { |
||||
|
CameraPositionMoveEndEvent(int mapId, CameraPosition value) |
||||
|
: super(mapId, value); |
||||
|
} |
||||
|
|
||||
|
///点击地图回调 |
||||
|
class MapTapEvent extends MapEvent<LatLng> { |
||||
|
MapTapEvent(int mapId, LatLng value) : super(mapId, value); |
||||
|
} |
||||
|
|
||||
|
///长按地图回调 |
||||
|
class MapLongPressEvent extends MapEvent<LatLng> { |
||||
|
MapLongPressEvent(int mapId, LatLng value) : super(mapId, value); |
||||
|
} |
||||
|
|
||||
|
/// 带位置回调的地图事件 |
||||
|
class _PositionedMapEvent<T> extends MapEvent<T> { |
||||
|
/// 事件中带的位置信息 |
||||
|
final LatLng position; |
||||
|
|
||||
|
/// 构造一个带位置的地图事件, |
||||
|
/// |
||||
|
/// `mapId` 当前地图的id |
||||
|
/// `value` 需要传输的值,可以为`null`. |
||||
|
_PositionedMapEvent(int mapId, this.position, T value) : super(mapId, value); |
||||
|
} |
||||
|
|
||||
|
/// [Marker] 的点击事件 |
||||
|
class MarkerTapEvent extends MapEvent<String> { |
||||
|
MarkerTapEvent(int mapId, String markerId) : super(mapId, markerId); |
||||
|
} |
||||
|
|
||||
|
/// [Marker] 的拖拽结束事件,附带拖拽结束时的位置信息[LatLng]. |
||||
|
class MarkerDragEndEvent extends _PositionedMapEvent<String> { |
||||
|
MarkerDragEndEvent(int mapId, LatLng position, String markerId) |
||||
|
: super(mapId, position, markerId); |
||||
|
} |
||||
|
|
||||
|
/// [Polyline] 的点击事件 |
||||
|
class PolylineTapEvent extends MapEvent<String> { |
||||
|
PolylineTapEvent(int mapId, String polylineId) : super(mapId, polylineId); |
||||
|
} |
||||
|
|
||||
|
/// Poi点击事件 |
||||
|
class MapPoiTouchEvent extends MapEvent<AMapPoi> { |
||||
|
MapPoiTouchEvent(int mapId, AMapPoi poi) : super(mapId, poi); |
||||
|
} |
||||
@ -0,0 +1,274 @@ |
|||||
|
import 'dart:async'; |
||||
|
import 'dart:typed_data'; |
||||
|
|
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'package:amap_flutter_map/src/core/amap_flutter_platform.dart'; |
||||
|
import 'package:amap_flutter_map/src/types/types.dart'; |
||||
|
import 'package:flutter/cupertino.dart'; |
||||
|
import 'package:flutter/foundation.dart'; |
||||
|
import 'package:flutter/gestures.dart'; |
||||
|
import 'package:flutter/services.dart'; |
||||
|
import 'package:stream_transform/stream_transform.dart'; |
||||
|
|
||||
|
import 'map_event.dart'; |
||||
|
|
||||
|
const VIEW_TYPE = 'com.amap.flutter.map'; |
||||
|
|
||||
|
/// 使用[MethodChannel]与Native代码通信的[AMapFlutterPlatform]的实现。 |
||||
|
class MethodChannelAMapFlutterMap implements AMapFlutterPlatform { |
||||
|
final Map<int, MethodChannel> _channels = {}; |
||||
|
|
||||
|
MethodChannel channel(int mapId) { |
||||
|
return _channels[mapId]!; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Future<void> init(int mapId) { |
||||
|
MethodChannel? channel = _channels[mapId]; |
||||
|
if (channel == null) { |
||||
|
channel = MethodChannel('amap_flutter_map_$mapId'); |
||||
|
channel.setMethodCallHandler((call) => _handleMethodCall(call, mapId)); |
||||
|
_channels[mapId] = channel; |
||||
|
} |
||||
|
return channel.invokeMethod<void>('map#waitForMap'); |
||||
|
} |
||||
|
|
||||
|
///更新地图参数 |
||||
|
Future<void> updateMapOptions( |
||||
|
Map<String, dynamic> newOptions, { |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<void>( |
||||
|
'map#update', |
||||
|
<String, dynamic>{ |
||||
|
'options': newOptions, |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/// 更新Marker的数据 |
||||
|
Future<void> updateMarkers( |
||||
|
MarkerUpdates markerUpdates, { |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<void>( |
||||
|
'markers#update', |
||||
|
markerUpdates.toMap(), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/// 更新polyline的数据 |
||||
|
Future<void> updatePolylines( |
||||
|
PolylineUpdates polylineUpdates, { |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<void>( |
||||
|
'polylines#update', |
||||
|
polylineUpdates.toMap(), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/// 更新polygon的数据 |
||||
|
Future<void> updatePolygons( |
||||
|
PolygonUpdates polygonUpdates, { |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<void>( |
||||
|
'polygons#update', |
||||
|
polygonUpdates.toMap(), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void dispose({required int id}) { |
||||
|
if (_channels.containsKey(id)) { |
||||
|
_channels.remove(id); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
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(), |
||||
|
); |
||||
|
} |
||||
|
return Text('当前平台:$defaultTargetPlatform, 不支持使用高德地图插件'); |
||||
|
} |
||||
|
|
||||
|
// handleMethodCall的`broadcast` |
||||
|
final StreamController<MapEvent> _mapEventStreamController = |
||||
|
StreamController<MapEvent>.broadcast(); |
||||
|
|
||||
|
// 根据mapid返回相应的event. |
||||
|
Stream<MapEvent> _events(int mapId) => |
||||
|
_mapEventStreamController.stream.where((event) => event.mapId == mapId); |
||||
|
|
||||
|
//定位回调 |
||||
|
Stream<LocationChangedEvent> onLocationChanged({required int mapId}) { |
||||
|
return _events(mapId).whereType<LocationChangedEvent>(); |
||||
|
} |
||||
|
|
||||
|
//Camera 移动回调 |
||||
|
Stream<CameraPositionMoveEvent> onCameraMove({required int mapId}) { |
||||
|
return _events(mapId).whereType<CameraPositionMoveEvent>(); |
||||
|
} |
||||
|
|
||||
|
///Camera 移动结束回调 |
||||
|
Stream<CameraPositionMoveEndEvent> onCameraMoveEnd({required int mapId}) { |
||||
|
return _events(mapId).whereType<CameraPositionMoveEndEvent>(); |
||||
|
} |
||||
|
|
||||
|
Stream<MapTapEvent> onMapTap({required int mapId}) { |
||||
|
return _events(mapId).whereType<MapTapEvent>(); |
||||
|
} |
||||
|
|
||||
|
Stream<MapLongPressEvent> onMapLongPress({required int mapId}) { |
||||
|
return _events(mapId).whereType<MapLongPressEvent>(); |
||||
|
} |
||||
|
|
||||
|
Stream<MapPoiTouchEvent> onPoiTouched({required int mapId}) { |
||||
|
return _events(mapId).whereType<MapPoiTouchEvent>(); |
||||
|
} |
||||
|
|
||||
|
Stream<MarkerTapEvent> onMarkerTap({required int mapId}) { |
||||
|
return _events(mapId).whereType<MarkerTapEvent>(); |
||||
|
} |
||||
|
|
||||
|
Stream<MarkerDragEndEvent> onMarkerDragEnd({required int mapId}) { |
||||
|
return _events(mapId).whereType<MarkerDragEndEvent>(); |
||||
|
} |
||||
|
|
||||
|
Stream<PolylineTapEvent> onPolylineTap({required int mapId}) { |
||||
|
return _events(mapId).whereType<PolylineTapEvent>(); |
||||
|
} |
||||
|
|
||||
|
Future<dynamic> _handleMethodCall(MethodCall call, int mapId) async { |
||||
|
switch (call.method) { |
||||
|
case 'location#changed': |
||||
|
try { |
||||
|
_mapEventStreamController.add(LocationChangedEvent( |
||||
|
mapId, AMapLocation.fromMap(call.arguments['location'])!)); |
||||
|
} catch (e) { |
||||
|
print("location#changed error=======>" + e.toString()); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case 'camera#onMove': |
||||
|
try { |
||||
|
_mapEventStreamController.add(CameraPositionMoveEvent( |
||||
|
mapId, CameraPosition.fromMap(call.arguments['position'])!)); |
||||
|
} catch (e) { |
||||
|
print("camera#onMove error===>" + e.toString()); |
||||
|
} |
||||
|
break; |
||||
|
case 'camera#onMoveEnd': |
||||
|
try { |
||||
|
_mapEventStreamController.add(CameraPositionMoveEndEvent( |
||||
|
mapId, CameraPosition.fromMap(call.arguments['position'])!)); |
||||
|
} catch (e) { |
||||
|
print("camera#onMoveEnd error===>" + e.toString()); |
||||
|
} |
||||
|
break; |
||||
|
case 'map#onTap': |
||||
|
_mapEventStreamController |
||||
|
.add(MapTapEvent(mapId, LatLng.fromJson(call.arguments['latLng'])!)); |
||||
|
break; |
||||
|
case 'map#onLongPress': |
||||
|
_mapEventStreamController.add(MapLongPressEvent( |
||||
|
mapId, LatLng.fromJson(call.arguments['latLng'])!)); |
||||
|
break; |
||||
|
|
||||
|
case 'marker#onTap': |
||||
|
_mapEventStreamController.add(MarkerTapEvent( |
||||
|
mapId, |
||||
|
call.arguments['markerId'], |
||||
|
)); |
||||
|
break; |
||||
|
case 'marker#onDragEnd': |
||||
|
_mapEventStreamController.add(MarkerDragEndEvent( |
||||
|
mapId, |
||||
|
LatLng.fromJson(call.arguments['position'])!, |
||||
|
call.arguments['markerId'])); |
||||
|
break; |
||||
|
case 'polyline#onTap': |
||||
|
_mapEventStreamController |
||||
|
.add(PolylineTapEvent(mapId, call.arguments['polylineId'])); |
||||
|
break; |
||||
|
case 'map#onPoiTouched': |
||||
|
try { |
||||
|
_mapEventStreamController.add( |
||||
|
MapPoiTouchEvent(mapId, AMapPoi.fromJson(call.arguments['poi'])!)); |
||||
|
} catch (e) { |
||||
|
print('map#onPoiTouched error===>' + e.toString()); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
///移动镜头到一个新的位置 |
||||
|
Future<void> moveCamera( |
||||
|
CameraUpdate cameraUpdate, { |
||||
|
required int mapId, |
||||
|
bool animated = true, |
||||
|
int duration = 0, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<void>('camera#move', <String, dynamic>{ |
||||
|
'cameraUpdate': cameraUpdate.toJson(), |
||||
|
'animated': animated, |
||||
|
'duration': duration |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
///设置地图每秒渲染的帧数 |
||||
|
Future<void> setRenderFps(int fps, {required int mapId}) { |
||||
|
return channel(mapId) |
||||
|
.invokeMethod<void>('map#setRenderFps', <String, dynamic>{ |
||||
|
'fps': fps, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
///截屏 |
||||
|
Future<Uint8List?> takeSnapshot({ |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<Uint8List>('map#takeSnapshot'); |
||||
|
} |
||||
|
|
||||
|
//获取地图审图号(普通地图) |
||||
|
Future<String?> getMapContentApprovalNumber({ |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<String>('map#contentApprovalNumber'); |
||||
|
} |
||||
|
|
||||
|
//获取地图审图号(卫星地图) |
||||
|
Future<String?> getSatelliteImageApprovalNumber({ |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId) |
||||
|
.invokeMethod<String>('map#satelliteImageApprovalNumber'); |
||||
|
} |
||||
|
|
||||
|
Future<void> clearDisk({ |
||||
|
required int mapId, |
||||
|
}) { |
||||
|
return channel(mapId).invokeMethod<void>('map#clearDisk'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
/// 地图覆盖物基类 |
||||
|
class BaseOverlay { |
||||
|
/// overlay id |
||||
|
late String _id; |
||||
|
|
||||
|
String get id => _id; |
||||
|
|
||||
|
BaseOverlay() { |
||||
|
this._id = this.hashCode.toString(); |
||||
|
} |
||||
|
|
||||
|
void setIdForCopy(String copyId) => _id = copyId; |
||||
|
|
||||
|
BaseOverlay clone() { |
||||
|
throw UnimplementedError( |
||||
|
'BaseOverlay subClass should implement this methed.'); |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> toMap() { |
||||
|
throw UnimplementedError( |
||||
|
'BaseOverlay subClass should implement this methed.'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
List<Map<String, dynamic>>? serializeOverlaySet(Set<BaseOverlay> overlays) { |
||||
|
return overlays |
||||
|
.map<Map<String, dynamic>>((BaseOverlay overlay) => overlay.toMap()) |
||||
|
.toList(); |
||||
|
} |
||||
@ -0,0 +1,140 @@ |
|||||
|
// Copyright 2018 The Chromium Authors. All rights reserved. |
||||
|
// Use of this source code is governed by a BSD-style license that can be |
||||
|
// found in the LICENSE file. |
||||
|
|
||||
|
import 'dart:async' show Future; |
||||
|
import 'dart:typed_data' show Uint8List; |
||||
|
import 'dart:ui'; |
||||
|
|
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart' show AMapUtil; |
||||
|
import 'package:flutter/material.dart' |
||||
|
show ImageConfiguration, AssetImage, AssetBundleImageKey; |
||||
|
import 'package:flutter/services.dart' show AssetBundle; |
||||
|
|
||||
|
import 'package:flutter/foundation.dart' show kIsWeb; |
||||
|
|
||||
|
/// Bitmap工具类 |
||||
|
class BitmapDescriptor { |
||||
|
const BitmapDescriptor._(this._json); |
||||
|
|
||||
|
/// 红色. |
||||
|
static const double hueRed = 0.0; |
||||
|
|
||||
|
/// 橙色. |
||||
|
static const double hueOrange = 30.0; |
||||
|
|
||||
|
/// 黄色. |
||||
|
static const double hueYellow = 60.0; |
||||
|
|
||||
|
/// 绿色. |
||||
|
static const double hueGreen = 120.0; |
||||
|
|
||||
|
/// 青色. |
||||
|
static const double hueCyan = 180.0; |
||||
|
|
||||
|
/// 天蓝色. |
||||
|
static const double hueAzure = 210.0; |
||||
|
|
||||
|
/// 蓝色. |
||||
|
static const double hueBlue = 240.0; |
||||
|
|
||||
|
/// 紫色. |
||||
|
static const double hueViolet = 270.0; |
||||
|
|
||||
|
/// 酒红色. |
||||
|
static const double hueMagenta = 300.0; |
||||
|
|
||||
|
/// 玫瑰红. |
||||
|
static const double hueRose = 330.0; |
||||
|
|
||||
|
/// 创建默认的marker 图标的 bitmap 描述信息对象. |
||||
|
static const BitmapDescriptor defaultMarker = |
||||
|
BitmapDescriptor._(<dynamic>['defaultMarker']); |
||||
|
|
||||
|
/// 创建引用默认着色的BitmapDescriptor |
||||
|
static BitmapDescriptor defaultMarkerWithHue(double hue) { |
||||
|
assert(0.0 <= hue && hue < 360.0); |
||||
|
String filename = "BLUE.png"; |
||||
|
if (hue == hueRed) { |
||||
|
filename = "RED.png"; |
||||
|
} else if (hue == hueOrange) { |
||||
|
filename = "ORANGE.png"; |
||||
|
} else if (hue == hueYellow) { |
||||
|
filename = "YELLOW.png"; |
||||
|
} else if (hue == hueGreen) { |
||||
|
filename = "GREEN.png"; |
||||
|
} else if (hue == hueCyan) { |
||||
|
filename = "CYAN.png"; |
||||
|
} else if (hue == hueAzure) { |
||||
|
filename = "AZURE.png"; |
||||
|
} else if (hue == hueBlue) { |
||||
|
filename = "BLUE.png"; |
||||
|
} else if (hue == hueViolet) { |
||||
|
filename = "VIOLET.png"; |
||||
|
} else if (hue == hueMagenta) { |
||||
|
filename = "MAGENTA.png"; |
||||
|
} else if (hue == hueRose) { |
||||
|
filename = "ROSE.png"; |
||||
|
} |
||||
|
return BitmapDescriptor._(<dynamic>[ |
||||
|
'fromAssetImage', |
||||
|
"packages/amap_flutter_map/res/$filename", |
||||
|
AMapUtil.devicePixelRatio |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
///根据输入的icon路径[iconPath]创建[BitmapDescriptor] |
||||
|
static BitmapDescriptor fromIconPath(String iconPath) { |
||||
|
return BitmapDescriptor._(<dynamic>[ |
||||
|
'fromAsset', |
||||
|
iconPath, |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
///从资源图像创建[BitmapDescriptor]。 |
||||
|
/// |
||||
|
///Flutter中的assert的资产图像按以下方式存储: |
||||
|
/// https://flutter.dev/docs/development/ui/assets and images声明-分辨率感知图像资源 |
||||
|
///该方法考虑了各种资产解决方案 |
||||
|
///并根据dpi将图像缩放到正确的分辨率。 |
||||
|
///将`mipmaps1设置为false可加载图像的精确dpi版本,默认情况下,`mipmap`为true。 |
||||
|
static Future<BitmapDescriptor> fromAssetImage( |
||||
|
ImageConfiguration configuration, |
||||
|
String assetName, { |
||||
|
AssetBundle? bundle, |
||||
|
String? package, |
||||
|
bool mipmaps = true, |
||||
|
}) async { |
||||
|
if (!mipmaps && configuration.devicePixelRatio != null) { |
||||
|
return BitmapDescriptor._(<dynamic>[ |
||||
|
'fromAssetImage', |
||||
|
assetName, |
||||
|
configuration.devicePixelRatio, |
||||
|
]); |
||||
|
} |
||||
|
final AssetImage assetImage = |
||||
|
AssetImage(assetName, package: package, bundle: bundle); |
||||
|
final AssetBundleImageKey assetBundleImageKey = |
||||
|
await assetImage.obtainKey(configuration); |
||||
|
final Size? size = configuration.size; |
||||
|
return BitmapDescriptor._(<dynamic>[ |
||||
|
'fromAssetImage', |
||||
|
assetBundleImageKey.name, |
||||
|
assetBundleImageKey.scale, |
||||
|
if (kIsWeb && size != null) |
||||
|
[ |
||||
|
size.width, |
||||
|
size.height, |
||||
|
], |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/// 根据将PNG图片转换后的二进制数据[byteData]创建BitmapDescriptor |
||||
|
static BitmapDescriptor fromBytes(Uint8List byteData) { |
||||
|
return BitmapDescriptor._(<dynamic>['fromBytes', byteData]); |
||||
|
} |
||||
|
|
||||
|
final dynamic _json; |
||||
|
|
||||
|
dynamic toMap() => _json; |
||||
|
} |
||||
@ -0,0 +1,150 @@ |
|||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'package:flutter/cupertino.dart'; |
||||
|
|
||||
|
/// 相机位置,包含可视区域的位置参数。 |
||||
|
class CameraPosition { |
||||
|
/// 构造一个CameraPosition 对象 |
||||
|
/// |
||||
|
/// 如果[bearing], [target], [tilt], 或者 [zoom] 为null时会返回[AssertionError] |
||||
|
const CameraPosition({ |
||||
|
this.bearing = 0.0, |
||||
|
required this.target, |
||||
|
this.tilt = 0.0, |
||||
|
this.zoom = 10, |
||||
|
}); |
||||
|
|
||||
|
/// 可视区域指向的方向,以角度为单位,从正北向逆时针方向计算,从0 度到360 度。 |
||||
|
final double bearing; |
||||
|
|
||||
|
/// 目标位置的屏幕中心点经纬度坐标。 |
||||
|
final LatLng target; |
||||
|
|
||||
|
/// 目标可视区域的倾斜度,以角度为单位。范围从0到360度 |
||||
|
final double tilt; |
||||
|
|
||||
|
/// 目标可视区域的缩放级别 |
||||
|
final double zoom; |
||||
|
|
||||
|
/// 将[CameraPosition]装换成Map |
||||
|
/// |
||||
|
/// 主要在插件内部使用 |
||||
|
dynamic toMap() => <String, dynamic>{ |
||||
|
'bearing': bearing, |
||||
|
'target': target.toJson(), |
||||
|
'tilt': tilt, |
||||
|
'zoom': zoom, |
||||
|
}; |
||||
|
|
||||
|
/// 从Map转换成[CameraPosition] |
||||
|
/// |
||||
|
/// 主要在插件内部使用 |
||||
|
static CameraPosition? fromMap(dynamic json) { |
||||
|
if (json == null || !(json is Map<dynamic, dynamic>)) { |
||||
|
return null; |
||||
|
} |
||||
|
final LatLng? target = LatLng.fromJson(json['target']); |
||||
|
if (target == null) { |
||||
|
return null; |
||||
|
} |
||||
|
return CameraPosition( |
||||
|
bearing: json['bearing'], |
||||
|
target: target, |
||||
|
tilt: json['tilt'], |
||||
|
zoom: json['zoom'], |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(dynamic other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (runtimeType != other.runtimeType) return false; |
||||
|
final CameraPosition typedOther = other; |
||||
|
return bearing == typedOther.bearing && |
||||
|
target == typedOther.target && |
||||
|
tilt == typedOther.tilt && |
||||
|
zoom == typedOther.zoom; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => Object.hash(bearing, target, tilt, zoom); |
||||
|
|
||||
|
@override |
||||
|
String toString() => |
||||
|
'CameraPosition(bearing: $bearing, target: $target, tilt: $tilt, zoom: $zoom)'; |
||||
|
} |
||||
|
|
||||
|
/// 描述地图状态将要发生的变化 |
||||
|
class CameraUpdate { |
||||
|
CameraUpdate._(this._json); |
||||
|
|
||||
|
///返回根据新的[CameraPosition]移动后的[CameraUpdate]. |
||||
|
/// |
||||
|
///主要用于改变地图的中心点、缩放级别、倾斜角、角度等信息 |
||||
|
static CameraUpdate newCameraPosition(CameraPosition cameraPosition) { |
||||
|
return CameraUpdate._(<dynamic>[ |
||||
|
'newCameraPosition', |
||||
|
cameraPosition.toMap(), |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
///移动到一个新的位置点[latLng] |
||||
|
/// |
||||
|
///主要用于改变地图的中心点 |
||||
|
static CameraUpdate newLatLng(LatLng latLng) { |
||||
|
return CameraUpdate._(<dynamic>['newLatLng', latLng.toJson()]); |
||||
|
} |
||||
|
|
||||
|
///根据指定到摄像头显示范围[bounds]和边界值[padding]创建一个CameraUpdate对象 |
||||
|
/// |
||||
|
///主要用于根据指定的显示范围[bounds]以最佳的视野显示地图 |
||||
|
static CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) { |
||||
|
return CameraUpdate._(<dynamic>[ |
||||
|
'newLatLngBounds', |
||||
|
bounds.toJson(), |
||||
|
padding, |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/// 根据指定的新的位置[latLng]和缩放级别[zoom]创建一个CameraUpdate对象 |
||||
|
/// |
||||
|
/// 主要用于同时改变中心点和缩放级别 |
||||
|
static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { |
||||
|
return CameraUpdate._(<dynamic>['newLatLngZoom', latLng.toJson(), zoom]); |
||||
|
} |
||||
|
|
||||
|
/// 按照指定到像素点[dx]和[dy]移动地图中心点 |
||||
|
/// |
||||
|
/// [dx]是水平移动的像素数。正值代表可视区域向右移动,负值代表可视区域向左移动 |
||||
|
/// |
||||
|
/// [dy]是垂直移动的像素数。正值代表可视区域向下移动,负值代表可视区域向上移动 |
||||
|
/// |
||||
|
/// 返回包含x,y方向上移动像素数的cameraUpdate对象。 |
||||
|
static CameraUpdate scrollBy(double dx, double dy) { |
||||
|
return CameraUpdate._(<dynamic>['scrollBy', dx, dy]); |
||||
|
} |
||||
|
|
||||
|
/// 创建一个在当前地图显示的级别基础上加1的CameraUpdate对象 |
||||
|
/// |
||||
|
///主要用于放大地图缩放级别,在当前地图显示的级别基础上加1 |
||||
|
static CameraUpdate zoomIn() { |
||||
|
return CameraUpdate._(<dynamic>['zoomIn']); |
||||
|
} |
||||
|
|
||||
|
/// 创建一个在当前地图显示的级别基础上加1的CameraUpdate对象 |
||||
|
/// |
||||
|
/// 主要用于减少地图缩放级别,在当前地图显示的级别基础上减1 |
||||
|
static CameraUpdate zoomOut() { |
||||
|
return CameraUpdate._(<dynamic>['zoomOut']); |
||||
|
} |
||||
|
|
||||
|
/// 创建一个指定缩放级别[zoom]的CameraUpdate对象 |
||||
|
/// |
||||
|
/// 主要用于设置地图缩放级别 |
||||
|
static CameraUpdate zoomTo(double zoom) { |
||||
|
return CameraUpdate._(<dynamic>['zoomTo', zoom]); |
||||
|
} |
||||
|
|
||||
|
final dynamic _json; |
||||
|
|
||||
|
dynamic toJson() => _json; |
||||
|
} |
||||
@ -0,0 +1,249 @@ |
|||||
|
// Copyright 2018 The Chromium Authors. All rights reserved. |
||||
|
// Use of this source code is governed by a BSD-style license that can be |
||||
|
// found in the LICENSE file. |
||||
|
|
||||
|
import 'dart:ui' show Offset; |
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'base_overlay.dart'; |
||||
|
import 'bitmap.dart'; |
||||
|
|
||||
|
/// Marker拖动回调 |
||||
|
typedef void MarkerDragEndCallback(String id, LatLng endPosition); |
||||
|
|
||||
|
///Marker的气泡 |
||||
|
/// |
||||
|
///Android和iOS的实现机制有差异,仅在接口层面拉齐,效果一致 |
||||
|
class InfoWindow { |
||||
|
/// 为 [Marker] 产生一个不可修改的文本气泡. |
||||
|
const InfoWindow({this.title, this.snippet}); |
||||
|
|
||||
|
/// 无文本的气泡 |
||||
|
static const InfoWindow noText = InfoWindow(); |
||||
|
|
||||
|
/// 气泡的title |
||||
|
final String? title; |
||||
|
|
||||
|
/// 气泡的详细信息 |
||||
|
final String? snippet; |
||||
|
|
||||
|
/// 气泡copy方法 |
||||
|
/// |
||||
|
InfoWindow copyWith({String? titleParam, String? snippetParam}) { |
||||
|
return InfoWindow( |
||||
|
title: titleParam ?? title, |
||||
|
snippet: snippetParam ?? snippet, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> _toMap() { |
||||
|
final Map<String, dynamic> json = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfPresent(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
json[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfPresent('title', title); |
||||
|
addIfPresent('snippet', snippet); |
||||
|
return json; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is! InfoWindow) { |
||||
|
return false; |
||||
|
} |
||||
|
final InfoWindow typedOther = other; |
||||
|
return title == typedOther.title && snippet == typedOther.snippet; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => Object.hash(title, snippet); |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return 'InfoWindow{title: $title, snippet: $snippet}'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 点覆盖物的类 |
||||
|
class Marker extends BaseOverlay { |
||||
|
Marker({ |
||||
|
required this.position, |
||||
|
double alpha = 1.0, |
||||
|
Offset anchor = const Offset(0.5, 1.0), |
||||
|
this.clickable = true, |
||||
|
this.draggable = false, |
||||
|
this.icon = BitmapDescriptor.defaultMarker, |
||||
|
this.infoWindowEnable = true, |
||||
|
this.infoWindow = InfoWindow.noText, |
||||
|
this.rotation = 0.0, |
||||
|
this.visible = true, |
||||
|
this.zIndex = 0.0, |
||||
|
this.onTap, |
||||
|
this.onDragEnd, |
||||
|
}) : this.alpha = |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
(alpha != null ? (alpha < 0 ? 0 : (alpha > 1 ? 1 : alpha)) : alpha), |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
this.anchor = |
||||
|
(anchor == null |
||||
|
? Offset(0.5, 1.0) |
||||
|
: ((anchor.dx < 0 || |
||||
|
anchor.dx > 1 || |
||||
|
anchor.dy < 0 || |
||||
|
anchor.dy > 1) |
||||
|
? Offset(0.5, 1.0) |
||||
|
: anchor)), |
||||
|
super(); |
||||
|
|
||||
|
/// 透明度 |
||||
|
final double alpha; |
||||
|
|
||||
|
/// 覆盖物视图相对地图上的经纬度位置的锚点 |
||||
|
final Offset anchor; |
||||
|
|
||||
|
/// 是否可点击,默认为true |
||||
|
final bool clickable; |
||||
|
|
||||
|
/// 是否可拖拽,默认为false |
||||
|
final bool draggable; |
||||
|
|
||||
|
/// 覆盖物的图标 |
||||
|
final BitmapDescriptor icon; |
||||
|
|
||||
|
/// 是否显示气泡,如果为true,则点击[Marker]后,会显示该气泡[InfoWindow] |
||||
|
/// 如果为false,则始终不会显示该气泡 |
||||
|
final bool infoWindowEnable; |
||||
|
|
||||
|
/// 覆盖物上的气泡,当被点击时,如果infoWindowEnable为true,则会显示出来 |
||||
|
final InfoWindow infoWindow; |
||||
|
|
||||
|
/// 位置,不能为空 |
||||
|
final LatLng position; |
||||
|
|
||||
|
/// 旋转角度,以锚点为中心,顺时针旋转(单位:度数) |
||||
|
/// |
||||
|
/// 注意:iOS端目前仅支持绕marker中心点旋转 |
||||
|
final double rotation; |
||||
|
|
||||
|
/// 是否可见 |
||||
|
final bool visible; |
||||
|
|
||||
|
/// z轴的值,用于调整该覆盖物的相对绘制层级关系 |
||||
|
/// 值越小,图层越靠下,iOS该值不支持动态修改,仅能在初始化时指定 |
||||
|
final double zIndex; |
||||
|
|
||||
|
/// 回调的参数是对应的id |
||||
|
final ArgumentCallback<String>? onTap; |
||||
|
|
||||
|
/// Marker被拖拽结束的回调 |
||||
|
final MarkerDragEndCallback? onDragEnd; |
||||
|
|
||||
|
/// copy的真正复制的参数,主要用于需要修改某个属性参数时使用 |
||||
|
Marker copyWith({ |
||||
|
double? alphaParam, |
||||
|
Offset? anchorParam, |
||||
|
bool? clickableParam, |
||||
|
bool? draggableParam, |
||||
|
BitmapDescriptor? iconParam, |
||||
|
bool? infoWindowEnableParam, |
||||
|
InfoWindow? infoWindowParam, |
||||
|
LatLng? positionParam, |
||||
|
double? rotationParam, |
||||
|
bool? visibleParam, |
||||
|
ArgumentCallback<String?>? onTapParam, |
||||
|
MarkerDragEndCallback? onDragEndParam, |
||||
|
}) { |
||||
|
Marker copyMark = Marker( |
||||
|
alpha: alphaParam ?? alpha, |
||||
|
anchor: anchorParam ?? anchor, |
||||
|
clickable: clickableParam ?? clickable, |
||||
|
draggable: draggableParam ?? draggable, |
||||
|
icon: iconParam ?? icon, |
||||
|
infoWindowEnable: infoWindowEnableParam ?? infoWindowEnable, |
||||
|
infoWindow: infoWindowParam ?? infoWindow, |
||||
|
position: positionParam ?? position, |
||||
|
rotation: rotationParam ?? rotation, |
||||
|
visible: visibleParam ?? visible, |
||||
|
zIndex: zIndex, |
||||
|
onTap: onTapParam ?? onTap, |
||||
|
onDragEnd: onDragEndParam ?? onDragEnd, |
||||
|
); |
||||
|
copyMark.setIdForCopy(id); |
||||
|
return copyMark; |
||||
|
} |
||||
|
|
||||
|
Marker clone() => copyWith(); |
||||
|
|
||||
|
@override |
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> json = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfPresent(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
json[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfPresent('id', id); |
||||
|
addIfPresent('alpha', alpha); |
||||
|
addIfPresent('anchor', _offsetToJson(anchor)); |
||||
|
addIfPresent('clickable', clickable); |
||||
|
addIfPresent('draggable', draggable); |
||||
|
addIfPresent('icon', icon.toMap()); |
||||
|
addIfPresent('infoWindowEnable', infoWindowEnable); |
||||
|
addIfPresent('infoWindow', infoWindow._toMap()); |
||||
|
addIfPresent('position', position.toJson()); |
||||
|
addIfPresent('rotation', rotation); |
||||
|
addIfPresent('visible', visible); |
||||
|
addIfPresent('zIndex', zIndex); |
||||
|
return json; |
||||
|
} |
||||
|
|
||||
|
dynamic _offsetToJson(Offset offset) { |
||||
|
return <dynamic>[offset.dx, offset.dy]; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is! Marker) return false; |
||||
|
final Marker typedOther = other; |
||||
|
return id == typedOther.id && |
||||
|
alpha == typedOther.alpha && |
||||
|
anchor == typedOther.anchor && |
||||
|
clickable == typedOther.clickable && |
||||
|
draggable == typedOther.draggable && |
||||
|
icon == typedOther.icon && |
||||
|
infoWindowEnable == typedOther.infoWindowEnable && |
||||
|
infoWindow == typedOther.infoWindow && |
||||
|
position == typedOther.position && |
||||
|
rotation == typedOther.rotation && |
||||
|
visible == typedOther.visible && |
||||
|
zIndex == typedOther.zIndex; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => super.hashCode; |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return 'Marker{id: $id, alpha: $alpha, anchor: $anchor, ' |
||||
|
'clickable: $clickable, draggable: $draggable,' |
||||
|
'icon: $icon, infoWindowEnable: $infoWindowEnable, infoWindow: $infoWindow, position: $position, rotation: $rotation, ' |
||||
|
'visible: $visible, zIndex: $zIndex, onTap: $onTap}'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Map<String, Marker> keyByMarkerId(Iterable<Marker> markers) { |
||||
|
return Map<String, Marker>.fromEntries( |
||||
|
markers.map( |
||||
|
(Marker marker) => MapEntry<String, Marker>(marker.id, marker.clone()), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
import 'dart:ui' show hashValues; |
||||
|
|
||||
|
import 'package:flutter/foundation.dart' show setEquals; |
||||
|
|
||||
|
import 'types.dart'; |
||||
|
import 'marker.dart'; |
||||
|
|
||||
|
/// 用以描述Marker的更新项 |
||||
|
class MarkerUpdates { |
||||
|
/// 根据之前的marker列表[previous]和当前的marker列表[current]创建[MakerUpdates]. |
||||
|
MarkerUpdates.from(Set<Marker> previous, Set<Marker> current) { |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (previous == null) { |
||||
|
previous = Set<Marker>.identity(); |
||||
|
} |
||||
|
|
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (current == null) { |
||||
|
current = Set<Marker>.identity(); |
||||
|
} |
||||
|
|
||||
|
final Map<String, Marker> previousMarkers = keyByMarkerId(previous); |
||||
|
final Map<String, Marker> currentMarkers = keyByMarkerId(current); |
||||
|
|
||||
|
final Set<String> prevMarkerIds = previousMarkers.keys.toSet(); |
||||
|
final Set<String> currentMarkerIds = currentMarkers.keys.toSet(); |
||||
|
|
||||
|
Marker idToCurrentMarker(String id) { |
||||
|
return currentMarkers[id]!; |
||||
|
} |
||||
|
|
||||
|
final Set<String> _markerIdsToRemove = prevMarkerIds.difference( |
||||
|
currentMarkerIds, |
||||
|
); |
||||
|
|
||||
|
final Set<Marker> _markersToAdd = |
||||
|
currentMarkerIds |
||||
|
.difference(prevMarkerIds) |
||||
|
.map(idToCurrentMarker) |
||||
|
.toSet(); |
||||
|
|
||||
|
bool hasChanged(Marker current) { |
||||
|
final Marker? previous = previousMarkers[current.id]; |
||||
|
return current != previous; |
||||
|
} |
||||
|
|
||||
|
final Set<Marker> _markersToChange = |
||||
|
currentMarkerIds |
||||
|
.intersection(prevMarkerIds) |
||||
|
.map(idToCurrentMarker) |
||||
|
.where(hasChanged) |
||||
|
.toSet(); |
||||
|
|
||||
|
markersToAdd = _markersToAdd; |
||||
|
markerIdsToRemove = _markerIdsToRemove; |
||||
|
markersToChange = _markersToChange; |
||||
|
} |
||||
|
|
||||
|
/// 想要添加的marker集合. |
||||
|
Set<Marker>? markersToAdd; |
||||
|
|
||||
|
/// 想要删除的marker的id集合 |
||||
|
Set<String>? markerIdsToRemove; |
||||
|
|
||||
|
/// 想要更新的marker集合. |
||||
|
Set<Marker>? markersToChange; |
||||
|
|
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> updateMap = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfNonNull(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
updateMap[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfNonNull('markersToAdd', serializeOverlaySet(markersToAdd!)); |
||||
|
addIfNonNull('markersToChange', serializeOverlaySet(markersToChange!)); |
||||
|
addIfNonNull('markerIdsToRemove', markerIdsToRemove?.toList()); |
||||
|
|
||||
|
return updateMap; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is! MarkerUpdates) return false; |
||||
|
final MarkerUpdates typedOther = other; |
||||
|
return setEquals(markersToAdd, typedOther.markersToAdd) && |
||||
|
setEquals(markerIdsToRemove, typedOther.markerIdsToRemove) && |
||||
|
setEquals(markersToChange, typedOther.markersToChange); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => |
||||
|
Object.hash(markersToAdd, markerIdsToRemove, markersToChange); |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return '_MarkerUpdates{markersToAdd: $markersToAdd, ' |
||||
|
'markerIdsToRemove: $markerIdsToRemove, ' |
||||
|
'markersToChange: $markersToChange}'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,120 @@ |
|||||
|
// Copyright 2019 The Chromium Authors. All rights reserved. |
||||
|
// Use of this source code is governed by a BSD-style license that can be |
||||
|
// found in the LICENSE file. |
||||
|
|
||||
|
import 'package:flutter/foundation.dart' show listEquals; |
||||
|
import 'package:flutter/material.dart' show Color; |
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'base_overlay.dart'; |
||||
|
import 'polyline.dart'; |
||||
|
|
||||
|
/// 线相关的覆盖物类,内部的属性,描述了覆盖物的纹理、颜色、线宽等特征 |
||||
|
class Polygon extends BaseOverlay { |
||||
|
/// 默认构造函数 |
||||
|
Polygon( |
||||
|
{required this.points, |
||||
|
double strokeWidth = 10, |
||||
|
this.strokeColor = const Color(0xCC00BFFF), |
||||
|
this.fillColor = const Color(0xC487CEFA), |
||||
|
this.visible = true, |
||||
|
this.joinType = JoinType.bevel}) |
||||
|
: assert(points.length > 0), |
||||
|
this.strokeWidth = (strokeWidth <= 0 ? 10 : strokeWidth), |
||||
|
super(); |
||||
|
|
||||
|
/// 覆盖物的坐标点数组,不能为空 |
||||
|
final List<LatLng> points; |
||||
|
|
||||
|
/// 边框宽度,单位为逻辑像素,同Android中的dp,iOS中的point |
||||
|
final double strokeWidth; |
||||
|
|
||||
|
/// 边框颜色,默认值为(0xCCC4E0F0) |
||||
|
final Color strokeColor; |
||||
|
|
||||
|
/// 填充颜色,默认值为(0xC4E0F0CC) |
||||
|
final Color fillColor; |
||||
|
|
||||
|
/// 是否可见 |
||||
|
final bool visible; |
||||
|
|
||||
|
/// 连接点类型,该参数不支持copy时修改,仅能在初始化时设置一次 |
||||
|
final JoinType joinType; |
||||
|
|
||||
|
/// 实际copy函数 |
||||
|
Polygon copyWith({ |
||||
|
List<LatLng>? pointsParam, |
||||
|
double? strokeWidthParam, |
||||
|
Color? strokeColorParam, |
||||
|
Color? fillColorParam, |
||||
|
bool? visibleParam, |
||||
|
}) { |
||||
|
Polygon copyPolyline = Polygon( |
||||
|
points: pointsParam ?? points, |
||||
|
strokeWidth: strokeWidthParam ?? strokeWidth, |
||||
|
strokeColor: strokeColorParam ?? strokeColor, |
||||
|
fillColor: fillColorParam ?? fillColor, |
||||
|
visible: visibleParam ?? visible, |
||||
|
joinType: joinType, |
||||
|
); |
||||
|
copyPolyline.setIdForCopy(id); |
||||
|
return copyPolyline; |
||||
|
} |
||||
|
|
||||
|
Polygon clone() => copyWith(); |
||||
|
|
||||
|
/// 转换成可以序列化的map |
||||
|
@override |
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> json = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfPresent(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
json[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfPresent('id', id); |
||||
|
json['points'] = _pointsToJson(); |
||||
|
addIfPresent('strokeWidth', strokeWidth); |
||||
|
addIfPresent('strokeColor', strokeColor.value); |
||||
|
addIfPresent('fillColor', fillColor.value); |
||||
|
addIfPresent('visible', visible); |
||||
|
addIfPresent('joinType', joinType.index); |
||||
|
return json; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is !Polygon) return false; |
||||
|
final Polygon typedOther = other; |
||||
|
return id == typedOther.id && |
||||
|
listEquals(points, typedOther.points) && |
||||
|
strokeWidth == typedOther.strokeWidth && |
||||
|
strokeColor == typedOther.strokeColor && |
||||
|
fillColor == typedOther.fillColor && |
||||
|
visible == typedOther.visible && |
||||
|
joinType == typedOther.joinType; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => super.hashCode; |
||||
|
|
||||
|
dynamic _pointsToJson() { |
||||
|
final List<dynamic> result = <dynamic>[]; |
||||
|
for (final LatLng point in points) { |
||||
|
result.add(point.toJson()); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Map<String, Polygon> keyByPolygonId(Iterable<Polygon> polylines) { |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (polylines == null) { |
||||
|
return <String, Polygon>{}; |
||||
|
} |
||||
|
return Map<String, Polygon>.fromEntries(polylines.map((Polygon polyline) => |
||||
|
MapEntry<String, Polygon>(polyline.id, polyline.clone()))); |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
// Copyright 2018 The Chromium Authors. All rights reserved. |
||||
|
// Use of this source code is governed by a BSD-style license that can be |
||||
|
// found in the LICENSE file. |
||||
|
|
||||
|
import 'dart:ui' show hashValues; |
||||
|
|
||||
|
import 'package:flutter/foundation.dart' show setEquals; |
||||
|
import 'types.dart'; |
||||
|
|
||||
|
/// 该类主要用以描述[Polygon]的增删改等更新操作 |
||||
|
class PolygonUpdates { |
||||
|
/// 通过Polygon的前后更新集合构造一个PolygonUpdates |
||||
|
PolygonUpdates.from(Set<Polygon> previous, Set<Polygon> current) { |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (previous == null) { |
||||
|
previous = Set<Polygon>.identity(); |
||||
|
} |
||||
|
|
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (current == null) { |
||||
|
current = Set<Polygon>.identity(); |
||||
|
} |
||||
|
|
||||
|
final Map<String, Polygon> previousPolygons = keyByPolygonId(previous); |
||||
|
final Map<String, Polygon> currentPolygons = keyByPolygonId(current); |
||||
|
|
||||
|
final Set<String> prevPolygonIds = previousPolygons.keys.toSet(); |
||||
|
final Set<String> currentPolygonIds = currentPolygons.keys.toSet(); |
||||
|
|
||||
|
Polygon idToCurrentPolygon(String id) { |
||||
|
return currentPolygons[id]!; |
||||
|
} |
||||
|
|
||||
|
final Set<String> _polygonIdsToRemove = prevPolygonIds.difference( |
||||
|
currentPolygonIds, |
||||
|
); |
||||
|
|
||||
|
final Set<Polygon> _polygonsToAdd = |
||||
|
currentPolygonIds |
||||
|
.difference(prevPolygonIds) |
||||
|
.map(idToCurrentPolygon) |
||||
|
.toSet(); |
||||
|
|
||||
|
bool hasChanged(Polygon current) { |
||||
|
final Polygon previous = previousPolygons[current.id]!; |
||||
|
return current != previous; |
||||
|
} |
||||
|
|
||||
|
final Set<Polygon> _polygonsToChange = |
||||
|
currentPolygonIds |
||||
|
.intersection(prevPolygonIds) |
||||
|
.map(idToCurrentPolygon) |
||||
|
.where(hasChanged) |
||||
|
.toSet(); |
||||
|
|
||||
|
polygonsToAdd = _polygonsToAdd; |
||||
|
polygonIdsToRemove = _polygonIdsToRemove; |
||||
|
polygonsToChange = _polygonsToChange; |
||||
|
} |
||||
|
|
||||
|
/// 想要添加的polygon对象集合. |
||||
|
Set<Polygon>? polygonsToAdd; |
||||
|
|
||||
|
/// 想要删除的polygon的id集合 |
||||
|
Set<String>? polygonIdsToRemove; |
||||
|
|
||||
|
/// 想要更新的polygon对象集合 |
||||
|
Set<Polygon>? polygonsToChange; |
||||
|
|
||||
|
/// 转换成可以序列化的map |
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> updateMap = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfNonNull(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
updateMap[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfNonNull('polygonsToAdd', serializeOverlaySet(polygonsToAdd!)); |
||||
|
addIfNonNull('polygonsToChange', serializeOverlaySet(polygonsToChange!)); |
||||
|
addIfNonNull('polygonIdsToRemove', polygonIdsToRemove?.toList()); |
||||
|
|
||||
|
return updateMap; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is! PolygonUpdates) return false; |
||||
|
final PolygonUpdates typedOther = other; |
||||
|
return setEquals(polygonsToAdd, typedOther.polygonsToAdd) && |
||||
|
setEquals(polygonIdsToRemove, typedOther.polygonIdsToRemove) && |
||||
|
setEquals(polygonsToChange, typedOther.polygonsToChange); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => |
||||
|
Object.hash(polygonsToAdd, polygonIdsToRemove, polygonsToChange); |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return '_PolygonUpdates{polygonsToAdd: $polygonsToAdd, ' |
||||
|
'polygonIdsToRemove: $polygonIdsToRemove, ' |
||||
|
'polygonsToChange: $polygonsToChange}'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,198 @@ |
|||||
|
// Copyright 2019 The Chromium Authors. All rights reserved. |
||||
|
// Use of this source code is governed by a BSD-style license that can be |
||||
|
// found in the LICENSE file. |
||||
|
|
||||
|
import 'package:amap_flutter_map/src/types/bitmap.dart'; |
||||
|
import 'package:flutter/foundation.dart' show listEquals; |
||||
|
import 'package:flutter/material.dart' show Color; |
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'base_overlay.dart'; |
||||
|
|
||||
|
/// 虚线类型 |
||||
|
enum DashLineType { |
||||
|
/// 不画虚线 |
||||
|
none, |
||||
|
|
||||
|
/// 方块样式 |
||||
|
square, |
||||
|
|
||||
|
/// 圆点样式 |
||||
|
circle, |
||||
|
} |
||||
|
|
||||
|
/// 线头类型 |
||||
|
enum CapType { |
||||
|
/// 普通头 |
||||
|
butt, |
||||
|
|
||||
|
/// 扩展头 |
||||
|
square, |
||||
|
|
||||
|
/// 箭头 |
||||
|
arrow, |
||||
|
|
||||
|
/// 圆形头 |
||||
|
round, |
||||
|
} |
||||
|
|
||||
|
/// 连接点类型 |
||||
|
enum JoinType { |
||||
|
/// 斜面连接点 |
||||
|
bevel, |
||||
|
|
||||
|
/// 斜接连接点 |
||||
|
miter, |
||||
|
|
||||
|
/// 圆角连接点 |
||||
|
round, |
||||
|
} |
||||
|
|
||||
|
/// 线相关的覆盖物类,内部的属性,描述了覆盖物的纹理、颜色、线宽等特征 |
||||
|
class Polyline extends BaseOverlay { |
||||
|
/// 默认构造函数 |
||||
|
Polyline({ |
||||
|
required this.points, |
||||
|
double width = 10, |
||||
|
this.visible = true, |
||||
|
this.geodesic = false, |
||||
|
double alpha = 1.0, |
||||
|
this.dashLineType = DashLineType.none, |
||||
|
this.capType = CapType.butt, |
||||
|
this.joinType = JoinType.bevel, |
||||
|
this.customTexture, |
||||
|
this.onTap, |
||||
|
this.color = const Color(0xCCC4E0F0), |
||||
|
}) : assert(points.length > 0), |
||||
|
this.width = (width <= 0 ? 10 : width), |
||||
|
this.alpha =(alpha < 0 ? 0 : (alpha > 1 ? 1 : alpha)), |
||||
|
super(); |
||||
|
|
||||
|
/// 覆盖物的坐标点数组,points不能为空 |
||||
|
final List<LatLng> points; |
||||
|
|
||||
|
/// 线宽,单位为逻辑像素,同Android中的dp,iOS中的point |
||||
|
final double width; |
||||
|
|
||||
|
/// 是否可见 |
||||
|
final bool visible; |
||||
|
|
||||
|
/// 透明度 |
||||
|
final double alpha; |
||||
|
|
||||
|
/// 覆盖物颜色,默认值为(0xCCC4E0F0). |
||||
|
final Color color; |
||||
|
|
||||
|
/// 自定义纹理图片,注意: 如果设置了自定义纹理图片,则color的设置将无效; |
||||
|
final BitmapDescriptor? customTexture; |
||||
|
|
||||
|
/// 是否为大地曲线 |
||||
|
final bool geodesic; |
||||
|
|
||||
|
/// 虚线类型 |
||||
|
final DashLineType dashLineType; |
||||
|
|
||||
|
/// 连接点类型 |
||||
|
final JoinType joinType; |
||||
|
|
||||
|
/// 线头类型 |
||||
|
final CapType capType; |
||||
|
|
||||
|
/// 点击回调(回调参数为id) |
||||
|
final ArgumentCallback<String>? onTap; |
||||
|
|
||||
|
/// 实际copy函数 |
||||
|
Polyline copyWith({ |
||||
|
List<LatLng>? pointsParam, |
||||
|
double? widthParam, |
||||
|
int? zIndexParam, |
||||
|
bool? visibleParam, |
||||
|
double? alphaParam, |
||||
|
DashLineType? dashLineTypeParam, |
||||
|
CapType? capTypeParam, |
||||
|
JoinType? joinTypeParam, |
||||
|
BitmapDescriptor? customTextureParam, |
||||
|
ArgumentCallback<String>? onTapParam, |
||||
|
Color? colorParam, |
||||
|
}) { |
||||
|
Polyline copyPolyline = Polyline( |
||||
|
points: pointsParam ?? points, |
||||
|
width: widthParam ?? width, |
||||
|
visible: visibleParam ?? visible, |
||||
|
geodesic: geodesic, |
||||
|
alpha: alphaParam ?? alpha, |
||||
|
dashLineType: dashLineTypeParam ?? dashLineType, |
||||
|
capType: capTypeParam ?? capType, |
||||
|
joinType: joinTypeParam ?? joinType, |
||||
|
customTexture: customTextureParam ?? customTexture, |
||||
|
onTap: onTapParam ?? onTap, |
||||
|
color: colorParam ?? color, |
||||
|
); |
||||
|
copyPolyline.setIdForCopy(id); |
||||
|
return copyPolyline; |
||||
|
} |
||||
|
|
||||
|
Polyline clone() => copyWith(); |
||||
|
|
||||
|
/// 将对象转换为可序列化的map. |
||||
|
@override |
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> json = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfPresent(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
json[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfPresent('id', id); |
||||
|
json['points'] = _pointsToJson(); |
||||
|
addIfPresent('width', width); |
||||
|
addIfPresent('visible', visible); |
||||
|
addIfPresent('geodesic', geodesic); |
||||
|
addIfPresent('alpha', alpha); |
||||
|
addIfPresent('dashLineType', dashLineType.index); |
||||
|
addIfPresent('capType', capType.index); |
||||
|
addIfPresent('joinType', joinType.index); |
||||
|
addIfPresent('customTexture', customTexture?.toMap()); |
||||
|
addIfPresent('color', color.value); |
||||
|
return json; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is !Polyline) return false; |
||||
|
final Polyline typedOther = other; |
||||
|
return id == typedOther.id && |
||||
|
listEquals(points, typedOther.points) && |
||||
|
width == typedOther.width && |
||||
|
visible == typedOther.visible && |
||||
|
geodesic == typedOther.geodesic && |
||||
|
alpha == typedOther.alpha && |
||||
|
dashLineType == typedOther.dashLineType && |
||||
|
capType == typedOther.capType && |
||||
|
joinType == typedOther.joinType && |
||||
|
color == typedOther.color; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => super.hashCode; |
||||
|
|
||||
|
dynamic _pointsToJson() { |
||||
|
final List<dynamic> result = <dynamic>[]; |
||||
|
for (final LatLng point in points) { |
||||
|
result.add(point.toJson()); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Map<String, Polyline> keyByPolylineId(Iterable<Polyline> polylines) { |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (polylines == null) { |
||||
|
return <String, Polyline>{}; |
||||
|
} |
||||
|
return Map<String, Polyline>.fromEntries(polylines.map((Polyline polyline) => |
||||
|
MapEntry<String, Polyline>(polyline.id, polyline.clone()))); |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
// Copyright 2018 The Chromium Authors. All rights reserved. |
||||
|
// Use of this source code is governed by a BSD-style license that can be |
||||
|
// found in the LICENSE file. |
||||
|
|
||||
|
import 'package:flutter/foundation.dart' show setEquals; |
||||
|
|
||||
|
import 'polyline.dart'; |
||||
|
import 'types.dart'; |
||||
|
|
||||
|
/// 该类主要用以描述[Polyline]的增删改等更新操作 |
||||
|
class PolylineUpdates { |
||||
|
/// 通过polyline的前后更新集合构造一个polylineUpdates |
||||
|
PolylineUpdates.from(Set<Polyline> previous, Set<Polyline> current) { |
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (previous == null) { |
||||
|
previous = Set<Polyline>.identity(); |
||||
|
} |
||||
|
|
||||
|
// ignore: unnecessary_null_comparison |
||||
|
if (current == null) { |
||||
|
current = Set<Polyline>.identity(); |
||||
|
} |
||||
|
|
||||
|
final Map<String, Polyline> previousPolylines = keyByPolylineId(previous); |
||||
|
final Map<String, Polyline> currentPolylines = keyByPolylineId(current); |
||||
|
|
||||
|
final Set<String> prevPolylineIds = previousPolylines.keys.toSet(); |
||||
|
final Set<String> currentPolylineIds = currentPolylines.keys.toSet(); |
||||
|
|
||||
|
Polyline idToCurrentPolyline(String id) { |
||||
|
return currentPolylines[id]!; |
||||
|
} |
||||
|
|
||||
|
final Set<String> _polylineIdsToRemove = prevPolylineIds.difference( |
||||
|
currentPolylineIds, |
||||
|
); |
||||
|
|
||||
|
final Set<Polyline> _polylinesToAdd = |
||||
|
currentPolylineIds |
||||
|
.difference(prevPolylineIds) |
||||
|
.map(idToCurrentPolyline) |
||||
|
.toSet(); |
||||
|
|
||||
|
bool hasChanged(Polyline current) { |
||||
|
final Polyline previous = previousPolylines[current.id]!; |
||||
|
return current != previous; |
||||
|
} |
||||
|
|
||||
|
final Set<Polyline> _polylinesToChange = |
||||
|
currentPolylineIds |
||||
|
.intersection(prevPolylineIds) |
||||
|
.map(idToCurrentPolyline) |
||||
|
.where(hasChanged) |
||||
|
.toSet(); |
||||
|
|
||||
|
polylinesToAdd = _polylinesToAdd; |
||||
|
polylineIdsToRemove = _polylineIdsToRemove; |
||||
|
polylinesToChange = _polylinesToChange; |
||||
|
} |
||||
|
|
||||
|
/// 用于添加polyline的集合 |
||||
|
Set<Polyline>? polylinesToAdd; |
||||
|
|
||||
|
/// 需要删除的plyline的id集合 |
||||
|
Set<String>? polylineIdsToRemove; |
||||
|
|
||||
|
/// 用于更新polyline的集合 |
||||
|
Set<Polyline>? polylinesToChange; |
||||
|
|
||||
|
/// 将对象装换为可序列化的对象 |
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> updateMap = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfNonNull(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
updateMap[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfNonNull('polylinesToAdd', serializeOverlaySet(polylinesToAdd!)); |
||||
|
addIfNonNull('polylinesToChange', serializeOverlaySet(polylinesToChange!)); |
||||
|
addIfNonNull('polylineIdsToRemove', polylineIdsToRemove?.toList()); |
||||
|
|
||||
|
return updateMap; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (other.runtimeType != runtimeType) return false; |
||||
|
if (other is! PolylineUpdates) return false; |
||||
|
final PolylineUpdates typedOther = other; |
||||
|
return setEquals(polylinesToAdd, typedOther.polylinesToAdd) && |
||||
|
setEquals(polylineIdsToRemove, typedOther.polylineIdsToRemove) && |
||||
|
setEquals(polylinesToChange, typedOther.polylinesToChange); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => |
||||
|
Object.hash(polylinesToAdd, polylineIdsToRemove, polylinesToChange); |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return '_PolylineUpdates{polylinesToAdd: $polylinesToAdd, ' |
||||
|
'polylineIdsToRemove: $polylineIdsToRemove, ' |
||||
|
'polylinesToChange: $polylinesToChange}'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
export 'camera.dart'; |
||||
|
export 'ui.dart'; |
||||
|
export 'base_overlay.dart'; |
||||
|
export 'marker.dart'; |
||||
|
export 'marker_updates.dart'; |
||||
|
export 'polyline.dart'; |
||||
|
export 'polyline_updates.dart'; |
||||
|
export 'polygon.dart'; |
||||
|
export 'polygon_updates.dart'; |
||||
|
export 'camera.dart'; |
||||
|
export 'bitmap.dart'; |
||||
@ -0,0 +1,257 @@ |
|||||
|
import 'dart:typed_data'; |
||||
|
import 'dart:ui' show Color; |
||||
|
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
||||
|
import 'package:amap_flutter_map/amap_flutter_map.dart'; |
||||
|
|
||||
|
/// 地图类型 |
||||
|
enum MapType { |
||||
|
/// 普通地图 |
||||
|
normal, |
||||
|
|
||||
|
/// 卫星地图 |
||||
|
satellite, |
||||
|
|
||||
|
/// 夜间视图 |
||||
|
night, |
||||
|
|
||||
|
/// 导航视图 |
||||
|
navi, |
||||
|
|
||||
|
/// 公交视图 |
||||
|
bus, |
||||
|
} |
||||
|
|
||||
|
// 设置摄像机的边界. |
||||
|
class CameraTargetBounds { |
||||
|
/// 使用指定的边界框或空值创建摄影机目标边界 |
||||
|
/// |
||||
|
/// 设置为null时代表不指定边界 |
||||
|
const CameraTargetBounds(this.bounds); |
||||
|
|
||||
|
/// 摄像机的边界. |
||||
|
/// |
||||
|
/// null代表不指定边界 |
||||
|
final LatLngBounds? bounds; |
||||
|
|
||||
|
/// 取消指定边界 |
||||
|
static const CameraTargetBounds unbounded = CameraTargetBounds(null); |
||||
|
|
||||
|
/// 转换成json对象 |
||||
|
dynamic toJson() => <dynamic>[bounds?.toJson()]; |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(dynamic other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (runtimeType != other.runtimeType) return false; |
||||
|
final CameraTargetBounds typedOther = other; |
||||
|
return bounds == typedOther.bounds; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => bounds.hashCode; |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return 'CameraTargetBounds(bounds: $bounds)'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 地图最大最小缩放级别的封装对象 |
||||
|
class MinMaxZoomPreference { |
||||
|
/// 为地图创建一个不可变的最大最小缩放范围 |
||||
|
/// |
||||
|
/// 缩放级别范围为[3, 20],超出范围取边界值 |
||||
|
/// |
||||
|
const MinMaxZoomPreference(double minZoom, double maxZoom) |
||||
|
: this.minZoom = |
||||
|
((minZoom < 3 ? 3 : minZoom) > (maxZoom > 20 ? 20 : maxZoom) |
||||
|
? maxZoom |
||||
|
: minZoom), |
||||
|
this.maxZoom = |
||||
|
((minZoom < 3 ? 3 : minZoom) > (maxZoom > 20 ? 20 : maxZoom) |
||||
|
? minZoom |
||||
|
: maxZoom); |
||||
|
|
||||
|
/// 最小zoomLevel |
||||
|
final double? minZoom; |
||||
|
|
||||
|
/// 最大zoomLevel |
||||
|
final double? maxZoom; |
||||
|
|
||||
|
/// 高德地图默认zoomLevel的范围. |
||||
|
static const MinMaxZoomPreference defaultPreference = MinMaxZoomPreference( |
||||
|
3, |
||||
|
20, |
||||
|
); |
||||
|
|
||||
|
/// JSON序列化. |
||||
|
dynamic toJson() => <dynamic>[minZoom, maxZoom]; |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(dynamic other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (runtimeType != other.runtimeType) return false; |
||||
|
final MinMaxZoomPreference typedOther = other; |
||||
|
return minZoom == typedOther.minZoom && maxZoom == typedOther.maxZoom; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => Object.hash(minZoom, maxZoom); |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return 'MinMaxZoomPreference(minZoom: $minZoom, maxZoom: $maxZoom)'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
///定位小蓝点配置项 |
||||
|
class MyLocationStyleOptions { |
||||
|
///是否显示定位小蓝点 |
||||
|
bool enabled; |
||||
|
|
||||
|
///精度圈填充色 |
||||
|
Color? circleFillColor; |
||||
|
|
||||
|
///精度圈边框色 |
||||
|
Color? circleStrokeColor; |
||||
|
|
||||
|
///精度圈边框宽度 |
||||
|
double? circleStrokeWidth; |
||||
|
|
||||
|
///小蓝点图标 |
||||
|
BitmapDescriptor? icon; |
||||
|
|
||||
|
MyLocationStyleOptions( |
||||
|
this.enabled, { |
||||
|
this.circleFillColor, |
||||
|
this.circleStrokeColor, |
||||
|
this.circleStrokeWidth, |
||||
|
this.icon, |
||||
|
}); |
||||
|
|
||||
|
MyLocationStyleOptions clone() { |
||||
|
return MyLocationStyleOptions( |
||||
|
enabled, |
||||
|
circleFillColor: circleFillColor, |
||||
|
circleStrokeColor: circleStrokeColor, |
||||
|
circleStrokeWidth: circleStrokeWidth, |
||||
|
icon: icon, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
static MyLocationStyleOptions? fromMap(dynamic json) { |
||||
|
if (null == json) { |
||||
|
return null; |
||||
|
} |
||||
|
return MyLocationStyleOptions( |
||||
|
json['enabled'] ?? false, |
||||
|
circleFillColor: json['circleFillColor'] ?? null, |
||||
|
circleStrokeColor: json['circleStrokeColor'] ?? null, |
||||
|
circleStrokeWidth: json['circleStrokeWidth'] ?? null, |
||||
|
icon: json['icon'] ?? null, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Map<String, dynamic> toMap() { |
||||
|
final Map<String, dynamic> json = <String, dynamic>{}; |
||||
|
|
||||
|
void addIfPresent(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
json[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfPresent('enabled', enabled); |
||||
|
addIfPresent('circleFillColor', circleFillColor?.value); |
||||
|
addIfPresent('circleStrokeColor', circleStrokeColor?.value); |
||||
|
addIfPresent('circleStrokeWidth', circleStrokeWidth); |
||||
|
addIfPresent('icon', icon?.toMap()); |
||||
|
return json; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (runtimeType != other.runtimeType) return false; |
||||
|
if (other is! MyLocationStyleOptions) return false; |
||||
|
final MyLocationStyleOptions typedOther = other; |
||||
|
return enabled == typedOther.enabled && |
||||
|
circleFillColor == typedOther.circleFillColor && |
||||
|
circleStrokeColor == typedOther.circleStrokeColor && |
||||
|
icon == typedOther.icon; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
String toString() { |
||||
|
return 'MyLocationOptionsStyle{' |
||||
|
'enabled: $enabled,' |
||||
|
'circleFillColor: $circleFillColor,' |
||||
|
'circleStrokeColor: $circleStrokeColor,' |
||||
|
'icon: $icon, }'; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => |
||||
|
Object.hash(enabled, circleFillColor, circleStrokeColor, icon); |
||||
|
} |
||||
|
|
||||
|
///地图自定义样式 |
||||
|
class CustomStyleOptions { |
||||
|
///开关项,是否开启自定义地图 |
||||
|
bool enabled; |
||||
|
|
||||
|
///自定义样式的二进制数据,对应下载的自定义地图文件中的style.data中的二进制数据 |
||||
|
Uint8List? styleData; |
||||
|
|
||||
|
///自定义扩展样式的二进制数据,对应下载的自定义地图文件中的style_extra.data中的二进制数据 |
||||
|
Uint8List? styleExtraData; |
||||
|
|
||||
|
CustomStyleOptions(this.enabled, {this.styleData, this.styleExtraData}); |
||||
|
|
||||
|
static CustomStyleOptions? fromMap(dynamic json) { |
||||
|
if (json == null) { |
||||
|
return null; |
||||
|
} |
||||
|
return CustomStyleOptions( |
||||
|
json['enabled'] ?? false, |
||||
|
styleData: json['styleData'] ?? null, |
||||
|
styleExtraData: json['styleExtraData'] ?? null, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
dynamic toMap() { |
||||
|
final Map<String, dynamic> json = <String, dynamic>{}; |
||||
|
void addIfPresent(String fieldName, dynamic value) { |
||||
|
if (value != null) { |
||||
|
json[fieldName] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addIfPresent('enabled', enabled); |
||||
|
addIfPresent('styleData', styleData); |
||||
|
addIfPresent('styleExtraData', styleExtraData); |
||||
|
return json; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) { |
||||
|
if (identical(this, other)) return true; |
||||
|
if (runtimeType != other.runtimeType) return false; |
||||
|
if (other is! CustomStyleOptions) return false; |
||||
|
final CustomStyleOptions typedOther = other; |
||||
|
return enabled == typedOther.enabled && |
||||
|
styleData == typedOther.styleData && |
||||
|
styleExtraData == typedOther.styleExtraData; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => Object.hash(enabled, styleData, styleExtraData); |
||||
|
|
||||
|
CustomStyleOptions clone() { |
||||
|
return CustomStyleOptions( |
||||
|
enabled, |
||||
|
styleData: styleData, |
||||
|
styleExtraData: styleExtraData, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -1,2 +1,2 @@ |
|||||
#Sun May 11 13:39:53 IST 2025 |
#Tue May 20 09:53:38 CST 2025 |
||||
gradle.version=8.10 |
gradle.version=8.9 |
||||
|
|||||
Loading…
Reference in new issue