init project
This commit is contained in:
150
lib/src/amap_controller.dart
Normal file
150
lib/src/amap_controller.dart
Normal file
@ -0,0 +1,150 @@
|
||||
part of amap_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);
|
||||
}
|
||||
}
|
419
lib/src/amap_widget.dart
Normal file
419
lib/src/amap_widget.dart
Normal file
@ -0,0 +1,419 @@
|
||||
part of amap_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;
|
||||
}
|
||||
}
|
||||
}
|
41
lib/src/core/amap_flutter_platform.dart
Normal file
41
lib/src/core/amap_flutter_platform.dart
Normal file
@ -0,0 +1,41 @@
|
||||
import 'package:amap_map/src/core/method_channel_amap_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_map”平台特定实现必须扩展的接口
|
||||
abstract class AMapFlutterPlatform extends PlatformInterface {
|
||||
static final Object _token = Object();
|
||||
AMapFlutterPlatform() : super(token: _token);
|
||||
static AMapFlutterPlatform _instance = MethodChannelAMapFlutterMap();
|
||||
|
||||
/// 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.');
|
||||
}
|
||||
}
|
77
lib/src/core/map_event.dart
Normal file
77
lib/src/core/map_event.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'package:amap_map/amap_map.dart';
|
||||
import 'package:x_amap_base/x_amap_base.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);
|
||||
}
|
273
lib/src/core/method_channel_amap_map.dart
Normal file
273
lib/src/core/method_channel_amap_map.dart
Normal file
@ -0,0 +1,273 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:x_amap_base/x_amap_base.dart';
|
||||
import 'package:amap_map/src/core/amap_flutter_platform.dart';
|
||||
import 'package:amap_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_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');
|
||||
}
|
||||
}
|
29
lib/src/types/base_overlay.dart
Normal file
29
lib/src/types/base_overlay.dart
Normal file
@ -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();
|
||||
}
|
140
lib/src/types/bitmap.dart
Normal file
140
lib/src/types/bitmap.dart
Normal file
@ -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:x_amap_base/x_amap_base.dart';
|
||||
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_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;
|
||||
}
|
153
lib/src/types/camera.dart
Normal file
153
lib/src/types/camera.dart
Normal file
@ -0,0 +1,153 @@
|
||||
import 'package:x_amap_base/x_amap_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 => hashValues(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;
|
||||
}
|
252
lib/src/types/marker.dart
Normal file
252
lib/src/types/marker.dart
Normal file
@ -0,0 +1,252 @@
|
||||
// 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, Offset;
|
||||
import 'package:amap_map/src/types/base_overlay.dart';
|
||||
import 'package:x_amap_base/x_amap_base.dart';
|
||||
import 'bitmap.dart';
|
||||
import 'base_overlay.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 => hashValues(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())));
|
||||
}
|
102
lib/src/types/marker_updates.dart
Normal file
102
lib/src/types/marker_updates.dart
Normal file
@ -0,0 +1,102 @@
|
||||
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 =>
|
||||
hashValues(markersToAdd, markerIdsToRemove, markersToChange);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '_MarkerUpdates{markersToAdd: $markersToAdd, '
|
||||
'markerIdsToRemove: $markerIdsToRemove, '
|
||||
'markersToChange: $markersToChange}';
|
||||
}
|
||||
}
|
120
lib/src/types/polygon.dart
Normal file
120
lib/src/types/polygon.dart
Normal file
@ -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:x_amap_base/x_amap_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())));
|
||||
}
|
105
lib/src/types/polygon_updates.dart
Normal file
105
lib/src/types/polygon_updates.dart
Normal file
@ -0,0 +1,105 @@
|
||||
// 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 =>
|
||||
hashValues(polygonsToAdd, polygonIdsToRemove, polygonsToChange);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '_PolygonUpdates{polygonsToAdd: $polygonsToAdd, '
|
||||
'polygonIdsToRemove: $polygonIdsToRemove, '
|
||||
'polygonsToChange: $polygonsToChange}';
|
||||
}
|
||||
}
|
198
lib/src/types/polyline.dart
Normal file
198
lib/src/types/polyline.dart
Normal file
@ -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_map/src/types/bitmap.dart';
|
||||
import 'package:flutter/foundation.dart' show listEquals;
|
||||
import 'package:flutter/material.dart' show Color;
|
||||
import 'package:x_amap_base/x_amap_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())));
|
||||
}
|
107
lib/src/types/polyline_updates.dart
Normal file
107
lib/src/types/polyline_updates.dart
Normal file
@ -0,0 +1,107 @@
|
||||
// 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 '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 =>
|
||||
hashValues(polylinesToAdd, polylineIdsToRemove, polylinesToChange);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '_PolylineUpdates{polylinesToAdd: $polylinesToAdd, '
|
||||
'polylineIdsToRemove: $polylineIdsToRemove, '
|
||||
'polylinesToChange: $polylinesToChange}';
|
||||
}
|
||||
}
|
10
lib/src/types/types.dart
Normal file
10
lib/src/types/types.dart
Normal file
@ -0,0 +1,10 @@
|
||||
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 'bitmap.dart';
|
256
lib/src/types/ui.dart
Normal file
256
lib/src/types/ui.dart
Normal file
@ -0,0 +1,256 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' show Color, hashValues;
|
||||
import 'package:x_amap_base/x_amap_base.dart';
|
||||
import 'package:amap_map/amap_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 => hashValues(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 =>
|
||||
hashValues(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 => hashValues(enabled, styleData, styleExtraData);
|
||||
|
||||
CustomStyleOptions clone() {
|
||||
return CustomStyleOptions(enabled,
|
||||
styleData: styleData, styleExtraData: styleExtraData);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user