init project

This commit is contained in:
Kuloud
2023-12-22 21:23:24 +08:00
commit 1fb3d91106
461 changed files with 58770 additions and 0 deletions

View 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
View 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;
}
}
}

View 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.');
}
}

View 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);
}

View 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');
}
}

View 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
View 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
View 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]是垂直移动的像素数。正值代表可视区域向下移动,负值代表可视区域向上移动
///
/// 返回包含xy方向上移动像素数的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
View 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())));
}

View 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
View 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中的dpiOS中的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())));
}

View 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
View 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中的dpiOS中的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())));
}

View 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
View 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
View 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);
}
}