35 changed files with 725 additions and 3014 deletions
@ -1,145 +0,0 @@ |
|||||
import 'dart:convert'; |
|
||||
import 'package:deep_voice/routes/app_routes.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:get/get.dart'; |
|
||||
|
|
||||
/// A widget that displays a navigation card with different modes of transportation. |
|
||||
Widget navigationCard( |
|
||||
BuildContext context, { |
|
||||
required List<Map<String, dynamic>> modes, |
|
||||
Map<String, dynamic>? navigation, |
|
||||
double height = 300, |
|
||||
}) { |
|
||||
int currentIndex = 0; |
|
||||
|
|
||||
return Container( |
|
||||
height: height, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(16), |
|
||||
color: Colors.white, |
|
||||
boxShadow: const [ |
|
||||
BoxShadow( |
|
||||
blurRadius: 8, |
|
||||
color: Colors.black12, |
|
||||
offset: Offset(0, 4), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
child: StatefulBuilder( |
|
||||
builder: (context, setInnerState) { |
|
||||
return Column( |
|
||||
crossAxisAlignment: CrossAxisAlignment.start, |
|
||||
children: [ |
|
||||
// 路线图片 |
|
||||
ClipRRect( |
|
||||
borderRadius: |
|
||||
const BorderRadius.vertical(top: Radius.circular(16)), |
|
||||
child: AnimatedSwitcher( |
|
||||
duration: const Duration(milliseconds: 300), |
|
||||
child: Image.network( |
|
||||
modes[currentIndex]["image"] as String, |
|
||||
key: ValueKey<String>(modes[currentIndex]["ntype"] as String), |
|
||||
height: 160, |
|
||||
width: double.infinity, |
|
||||
fit: BoxFit.cover, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
|
|
||||
// 导航方式切换按钮 |
|
||||
Padding( |
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: List.generate(modes.length, (index) { |
|
||||
final isSelected = index == currentIndex; |
|
||||
return GestureDetector( |
|
||||
onTap: () { |
|
||||
setInnerState(() => currentIndex = index); |
|
||||
}, |
|
||||
child: SizedBox( |
|
||||
width: 50, |
|
||||
child: Text( |
|
||||
modes[index]["ntype"] as String, |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: isSelected ? Colors.blue : Colors.black87, |
|
||||
fontSize: 12, |
|
||||
fontWeight: FontWeight.w500, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
}), |
|
||||
), |
|
||||
), |
|
||||
|
|
||||
// 距离和耗时 |
|
||||
Padding( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
Text('距离:${modes[currentIndex]["distance"]}', |
|
||||
style: const TextStyle(fontSize: 12, color: Colors.blue)), |
|
||||
IconButton( |
|
||||
icon: const Icon(Icons.turn_left, |
|
||||
color: Colors.blue, size: 20), |
|
||||
onPressed: () { |
|
||||
Get.toNamed(Routes.navigation); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
|
|
||||
// 导航实时数据显示 |
|
||||
if (navigation != null) |
|
||||
Padding( |
|
||||
padding: const EdgeInsets.all(8), |
|
||||
child: Row( |
|
||||
crossAxisAlignment: CrossAxisAlignment.center, |
|
||||
children: [ |
|
||||
// 圆形方向图标(如果有) |
|
||||
if (navigation["directionIcon"] != "") |
|
||||
Container( |
|
||||
width: 32, |
|
||||
height: 32, |
|
||||
decoration: const BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
color: Colors.blue, |
|
||||
), |
|
||||
child: ClipOval( |
|
||||
child: Image.memory( |
|
||||
base64Decode(navigation["directionIcon"]), |
|
||||
fit: BoxFit.cover, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(width: 8), |
|
||||
// 文字信息 |
|
||||
Expanded( |
|
||||
child: Column( |
|
||||
crossAxisAlignment: CrossAxisAlignment.start, |
|
||||
children: [ |
|
||||
if (navigation["currentRoad"] != null) |
|
||||
Text('当前道路:${navigation["currentRoad"]}', |
|
||||
style: const TextStyle(fontSize: 12)), |
|
||||
if (navigation["nextRoad"] != null) |
|
||||
Text('下一道路:${navigation["nextRoad"]}', |
|
||||
style: const TextStyle(fontSize: 12)), |
|
||||
if (navigation["distanceText"] != null) |
|
||||
Text('剩余距离:${navigation["distanceText"]}', |
|
||||
style: const TextStyle(fontSize: 12)), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
@ -0,0 +1,254 @@ |
|||||
|
package com.yunqiinnovation.amap_service |
||||
|
|
||||
|
import android.os.Bundle |
||||
|
import androidx.appcompat.app.AppCompatActivity |
||||
|
import com.amap.api.navi.AMapNavi |
||||
|
import com.amap.api.navi.AMapNaviListener |
||||
|
import com.amap.api.navi.AMapNaviView |
||||
|
import com.amap.api.navi.AMapNaviViewListener |
||||
|
import com.amap.api.navi.model.AMapCalcRouteResult |
||||
|
import com.amap.api.navi.model.AMapLaneInfo |
||||
|
import com.amap.api.navi.model.AMapModelCross |
||||
|
import com.amap.api.navi.model.AMapNaviCameraInfo |
||||
|
import com.amap.api.navi.model.AMapNaviCross |
||||
|
import com.amap.api.navi.model.AMapNaviLocation |
||||
|
import com.amap.api.navi.model.AMapNaviRouteNotifyData |
||||
|
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo |
||||
|
import com.amap.api.navi.model.AMapServiceAreaInfo |
||||
|
import com.amap.api.navi.model.AimLessModeCongestionInfo |
||||
|
import com.amap.api.navi.model.AimLessModeStat |
||||
|
import com.amap.api.navi.model.NaviInfo |
||||
|
|
||||
|
class NavigationActivity : AppCompatActivity(), AMapNaviListener, AMapNaviViewListener { |
||||
|
private lateinit var naviView: AMapNaviView |
||||
|
private var navi: AMapNavi? = null |
||||
|
|
||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||
|
super.onCreate(savedInstanceState) |
||||
|
setContentView(R.layout.activity_navigation) |
||||
|
|
||||
|
// 初始化导航视图 |
||||
|
naviView = findViewById(R.id.navi_view) |
||||
|
naviView.onCreate(savedInstanceState) |
||||
|
naviView.setAMapNaviViewListener(this) |
||||
|
|
||||
|
navi = NavigationService.sharedNavi |
||||
|
if (navi == null) { |
||||
|
finish() // 服务未启动,退出 |
||||
|
return |
||||
|
} |
||||
|
navi?.addAMapNaviListener(this); |
||||
|
|
||||
|
// 绑定当前 activity 到导航状态 |
||||
|
} |
||||
|
|
||||
|
override fun onResume() { |
||||
|
super.onResume() |
||||
|
naviView.onResume() |
||||
|
} |
||||
|
|
||||
|
override fun onPause() { |
||||
|
super.onPause() |
||||
|
naviView.onPause() |
||||
|
} |
||||
|
|
||||
|
override fun onDestroy() { |
||||
|
super.onDestroy() |
||||
|
naviView.onDestroy() |
||||
|
navi?.removeAMapNaviListener(this); |
||||
|
} |
||||
|
|
||||
|
override fun onInitNaviFailure() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onInitNaviSuccess() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onStartNavi(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onTrafficStatusUpdate() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onLocationChange(p0: AMapNaviLocation?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onGetNavigationText(p0: Int, p1: String?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onGetNavigationText(p0: String?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onEndEmulatorNavi() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onArriveDestination() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onCalculateRouteFailure(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onCalculateRouteFailure(p0: AMapCalcRouteResult?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onReCalculateRouteForYaw() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onReCalculateRouteForTrafficJam() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onArrivedWayPoint(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onGpsOpenStatus(p0: Boolean) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviInfoUpdate(p0: NaviInfo?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun updateCameraInfo(p0: Array<out AMapNaviCameraInfo>?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun updateIntervalCameraInfo( |
||||
|
p0: AMapNaviCameraInfo?, |
||||
|
p1: AMapNaviCameraInfo?, |
||||
|
p2: Int |
||||
|
) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onServiceAreaUpdate(p0: Array<out AMapServiceAreaInfo>?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun showCross(p0: AMapNaviCross?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun hideCross() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun showModeCross(p0: AMapModelCross?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun hideModeCross() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun showLaneInfo(p0: Array<out AMapLaneInfo>?, p1: ByteArray?, p2: ByteArray?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun showLaneInfo(p0: AMapLaneInfo?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun hideLaneInfo() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onCalculateRouteSuccess(p0: IntArray?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onCalculateRouteSuccess(p0: AMapCalcRouteResult?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun notifyParallelRoad(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun OnUpdateTrafficFacility(p0: Array<out AMapNaviTrafficFacilityInfo>?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun OnUpdateTrafficFacility(p0: AMapNaviTrafficFacilityInfo?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun updateAimlessModeStatistics(p0: AimLessModeStat?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun updateAimlessModeCongestionInfo(p0: AimLessModeCongestionInfo?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onPlayRing(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviRouteNotify(p0: AMapNaviRouteNotifyData?) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onGpsSignalWeak(p0: Boolean) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
//UI事件---------------------------------------------------------------------------------------------- |
||||
|
override fun onNaviSetting() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviCancel() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviBackClick(): Boolean { |
||||
|
// TODO("Not yet implemented") |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
override fun onNaviMapMode(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviTurnClick() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNextRoadClick() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onScanViewButtonClick() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onLockMap(p0: Boolean) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviViewLoaded() { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onMapTypeChanged(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
|
||||
|
override fun onNaviViewShowMode(p0: Int) { |
||||
|
// TODO("Not yet implemented") |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
<com.amap.api.navi.AMapNaviView |
||||
|
android:id="@+id/navi_view" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"/> |
||||
|
</FrameLayout> |
||||
@ -0,0 +1,9 @@ |
|||||
|
<resources> |
||||
|
<!-- 基础应用主题 --> |
||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> |
||||
|
<!-- 自定义主题属性 --> |
||||
|
<item name="colorPrimary">@color/colorPrimary</item> |
||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> |
||||
|
<item name="colorAccent">@color/colorAccent</item> |
||||
|
</style> |
||||
|
</resources> |
||||
@ -1,174 +0,0 @@ |
|||||
part of amap_service_map; |
|
||||
|
|
||||
final MethodChannelAMapFlutterMap _methodChannel = |
|
||||
AMapFlutterPlatform.instance as MethodChannelAMapFlutterMap; |
|
||||
|
|
||||
/// 地图通信中心 |
|
||||
class AMapController { |
|
||||
final int mapId; |
|
||||
final _MapState _mapState; |
|
||||
|
|
||||
AMapController._( |
|
||||
CameraPosition initCameraPosition, |
|
||||
this._mapState, { |
|
||||
required this.mapId, |
|
||||
}) { |
|
||||
_connectStreams(mapId); |
|
||||
} |
|
||||
|
|
||||
///根据传入的id初始化[AMapController] |
|
||||
/// 主要用于在[AMapWidget]初始化时在[AMapWidget.onMapCreated]中初始化controller |
|
||||
static Future<AMapController> init( |
|
||||
int id, |
|
||||
CameraPosition initialCameration, |
|
||||
_MapState mapState, |
|
||||
) async { |
|
||||
await _methodChannel.init(id); |
|
||||
return AMapController._(initialCameration, mapState, mapId: id); |
|
||||
} |
|
||||
|
|
||||
///只用于测试 |
|
||||
///用于与native的通信 |
|
||||
@visibleForTesting |
|
||||
MethodChannel get channel { |
|
||||
return _methodChannel.channel(mapId); |
|
||||
} |
|
||||
|
|
||||
void _connectStreams(int mapId) { |
|
||||
if (_mapState.widget.onLocationChanged != null) { |
|
||||
_methodChannel |
|
||||
.onLocationChanged(mapId: mapId) |
|
||||
.listen( |
|
||||
(LocationChangedEvent e) => |
|
||||
_mapState.widget.onLocationChanged!(e.value), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
if (_mapState.widget.onCameraMove != null) { |
|
||||
_methodChannel |
|
||||
.onCameraMove(mapId: mapId) |
|
||||
.listen( |
|
||||
(CameraPositionMoveEvent e) => |
|
||||
_mapState.widget.onCameraMove!(e.value), |
|
||||
); |
|
||||
} |
|
||||
if (_mapState.widget.onCameraMoveEnd != null) { |
|
||||
_methodChannel |
|
||||
.onCameraMoveEnd(mapId: mapId) |
|
||||
.listen( |
|
||||
(CameraPositionMoveEndEvent e) => |
|
||||
_mapState.widget.onCameraMoveEnd!(e.value), |
|
||||
); |
|
||||
} |
|
||||
if (_mapState.widget.onTap != null) { |
|
||||
_methodChannel |
|
||||
.onMapTap(mapId: mapId) |
|
||||
.listen(((MapTapEvent e) => _mapState.widget.onTap!(e.value))); |
|
||||
} |
|
||||
if (_mapState.widget.onLongPress != null) { |
|
||||
_methodChannel |
|
||||
.onMapLongPress(mapId: mapId) |
|
||||
.listen( |
|
||||
((MapLongPressEvent e) => _mapState.widget.onLongPress!(e.value)), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
if (_mapState.widget.onPoiTouched != null) { |
|
||||
_methodChannel |
|
||||
.onPoiTouched(mapId: mapId) |
|
||||
.listen( |
|
||||
((MapPoiTouchEvent e) => _mapState.widget.onPoiTouched!(e.value)), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
_methodChannel |
|
||||
.onMarkerTap(mapId: mapId) |
|
||||
.listen((MarkerTapEvent e) => _mapState.onMarkerTap(e.value)); |
|
||||
|
|
||||
_methodChannel |
|
||||
.onMarkerDragEnd(mapId: mapId) |
|
||||
.listen( |
|
||||
(MarkerDragEndEvent e) => |
|
||||
_mapState.onMarkerDragEnd(e.value, e.position), |
|
||||
); |
|
||||
|
|
||||
_methodChannel |
|
||||
.onPolylineTap(mapId: mapId) |
|
||||
.listen((PolylineTapEvent e) => _mapState.onPolylineTap(e.value)); |
|
||||
} |
|
||||
|
|
||||
void disponse() { |
|
||||
_methodChannel.dispose(id: mapId); |
|
||||
} |
|
||||
|
|
||||
Future<void> _updateMapOptions(Map<String, dynamic> optionsUpdate) { |
|
||||
return _methodChannel.updateMapOptions(optionsUpdate, mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
Future<void> _updateMarkers(MarkerUpdates markerUpdates) { |
|
||||
return _methodChannel.updateMarkers(markerUpdates, mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
Future<void> _updatePolylines(PolylineUpdates polylineUpdates) { |
|
||||
return _methodChannel.updatePolylines(polylineUpdates, mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
Future<void> _updatePolygons(PolygonUpdates polygonUpdates) { |
|
||||
return _methodChannel.updatePolygons(polygonUpdates, mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
///改变地图视角 |
|
||||
/// |
|
||||
///通过[CameraUpdate]对象设置新的中心点、缩放比例、放大缩小、显示区域等内容 |
|
||||
/// |
|
||||
///(注意:iOS端设置显示区域时,不支持duration参数,动画时长使用iOS地图默认值350毫秒) |
|
||||
/// |
|
||||
///可选属性[animated]用于控制是否执行动画移动 |
|
||||
/// |
|
||||
///可选属性[duration]用于控制执行动画的时长,默认250毫秒,单位:毫秒 |
|
||||
Future<void> moveCamera( |
|
||||
CameraUpdate cameraUpdate, { |
|
||||
bool animated = true, |
|
||||
int duration = 250, |
|
||||
}) { |
|
||||
return _methodChannel.moveCamera( |
|
||||
cameraUpdate, |
|
||||
mapId: mapId, |
|
||||
animated: animated, |
|
||||
duration: duration, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
///设置地图每秒渲染的帧数 |
|
||||
Future<void> setRenderFps(int fps) { |
|
||||
return _methodChannel.setRenderFps(fps, mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
///地图截屏 |
|
||||
Future<Uint8List?> takeSnapshot() { |
|
||||
return _methodChannel.takeSnapshot(mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
/// 获取地图审图号(普通地图) |
|
||||
/// |
|
||||
/// 任何使用高德地图API调用地图服务的应用必须在其应用中对外透出审图号 |
|
||||
/// |
|
||||
/// 如高德地图在"关于"中体现 |
|
||||
Future<String?> getMapContentApprovalNumber() { |
|
||||
return _methodChannel.getMapContentApprovalNumber(mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
/// 获取地图审图号(卫星地图) |
|
||||
/// |
|
||||
/// 任何使用高德地图API调用地图服务的应用必须在其应用中对外透出审图号 |
|
||||
/// |
|
||||
/// 如高德地图在"关于"中体现 |
|
||||
Future<String?> getSatelliteImageApprovalNumber() { |
|
||||
return _methodChannel.getSatelliteImageApprovalNumber(mapId: mapId); |
|
||||
} |
|
||||
|
|
||||
/// 清空缓存 |
|
||||
Future<void> clearDisk() { |
|
||||
return _methodChannel.clearDisk(mapId: mapId); |
|
||||
} |
|
||||
} |
|
||||
@ -1,20 +0,0 @@ |
|||||
/// 高德地图Flutter插件入口文件 |
|
||||
library amap_service_map; |
|
||||
|
|
||||
import 'dart:async'; |
|
||||
import 'dart:typed_data'; |
|
||||
|
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'package:amap_flutter_map/src/core/amap_flutter_platform.dart'; |
|
||||
import 'package:amap_flutter_map/src/core/map_event.dart'; |
|
||||
import 'package:amap_flutter_map/src/core/method_channel_amap_flutter_map.dart'; |
|
||||
import 'package:amap_flutter_map/src/types/types.dart'; |
|
||||
import 'package:flutter/foundation.dart'; |
|
||||
import 'package:flutter/gestures.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
|
|
||||
export 'package:amap_flutter_map/src/types/types.dart'; |
|
||||
|
|
||||
part 'amap_controller.dart'; |
|
||||
part 'amap_widget.dart'; |
|
||||
@ -1,425 +0,0 @@ |
|||||
part of amap_service_map; |
|
||||
|
|
||||
typedef void MapCreatedCallback(AMapController controller); |
|
||||
|
|
||||
///用于展示高德地图的Widget |
|
||||
class AMapWidget extends StatefulWidget { |
|
||||
///高德开放平台的key |
|
||||
/// |
|
||||
final AMapApiKey? apiKey; |
|
||||
|
|
||||
/// 初始化时的地图中心点 |
|
||||
final CameraPosition initialCameraPosition; |
|
||||
|
|
||||
///地图类型 |
|
||||
final MapType mapType; |
|
||||
|
|
||||
///自定义地图样式 |
|
||||
final CustomStyleOptions? customStyleOptions; |
|
||||
|
|
||||
///定位小蓝点 |
|
||||
final MyLocationStyleOptions? myLocationStyleOptions; |
|
||||
|
|
||||
///缩放级别范围 |
|
||||
final MinMaxZoomPreference? minMaxZoomPreference; |
|
||||
|
|
||||
///地图显示范围 |
|
||||
final LatLngBounds? limitBounds; |
|
||||
|
|
||||
///显示路况开关 |
|
||||
final bool trafficEnabled; |
|
||||
|
|
||||
/// 地图poi是否允许点击 |
|
||||
final bool touchPoiEnabled; |
|
||||
|
|
||||
///是否显示3D建筑物 |
|
||||
final bool buildingsEnabled; |
|
||||
|
|
||||
///是否显示底图文字标注 |
|
||||
final bool labelsEnabled; |
|
||||
|
|
||||
///是否显示指南针 |
|
||||
final bool compassEnabled; |
|
||||
|
|
||||
///是否显示比例尺 |
|
||||
final bool scaleEnabled; |
|
||||
|
|
||||
///是否支持缩放手势 |
|
||||
final bool zoomGesturesEnabled; |
|
||||
|
|
||||
///是否支持滑动手势 |
|
||||
final bool scrollGesturesEnabled; |
|
||||
|
|
||||
///是否支持旋转手势 |
|
||||
final bool rotateGesturesEnabled; |
|
||||
|
|
||||
///是否支持倾斜手势 |
|
||||
final bool tiltGesturesEnabled; |
|
||||
|
|
||||
/// 地图上显示的Marker |
|
||||
final Set<Marker> markers; |
|
||||
|
|
||||
/// 地图上显示的polyline |
|
||||
final Set<Polyline> polylines; |
|
||||
|
|
||||
/// 地图上显示的polygon |
|
||||
final Set<Polygon> polygons; |
|
||||
|
|
||||
/// 地图创建成功的回调, 收到此回调之后才可以操作地图 |
|
||||
final MapCreatedCallback? onMapCreated; |
|
||||
|
|
||||
/// 相机视角持续移动的回调 |
|
||||
final ArgumentCallback<CameraPosition>? onCameraMove; |
|
||||
|
|
||||
/// 相机视角移动结束的回调 |
|
||||
final ArgumentCallback<CameraPosition>? onCameraMoveEnd; |
|
||||
|
|
||||
/// 地图单击事件的回调 |
|
||||
final ArgumentCallback<LatLng>? onTap; |
|
||||
|
|
||||
/// 地图长按事件的回调 |
|
||||
final ArgumentCallback<LatLng>? onLongPress; |
|
||||
|
|
||||
/// 地图POI的点击回调,需要`touchPoiEnabled`true,才能回调 |
|
||||
final ArgumentCallback<AMapPoi>? onPoiTouched; |
|
||||
|
|
||||
///位置回调 |
|
||||
final ArgumentCallback<AMapLocation>? onLocationChanged; |
|
||||
|
|
||||
///需要应用到地图上的手势集合 |
|
||||
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers; |
|
||||
|
|
||||
///高德合规声明配置 |
|
||||
/// |
|
||||
/// 高德SDK合规使用方案请参考:https://lbs.amap.com/news/sdkhgsy |
|
||||
final AMapPrivacyStatement? privacyStatement; |
|
||||
|
|
||||
/// 创建一个展示高德地图的widget |
|
||||
/// |
|
||||
/// 如果使用的高德地图SDK的版本是8.1.0及以上版本, |
|
||||
/// 在app首次启动时必须传入高德合规声明配置[privacyStatement],后续如果没有变化不需要重复设置 |
|
||||
/// <li>[privacyStatement.hasContains] 隐私权政策是否包含高德开平隐私权政策</li> |
|
||||
/// <li>[privacyStatement.hasShow] 是否已经弹窗展示给用户</li> |
|
||||
/// <li>[privacyStatement.hasAgree] 隐私权政策是否已经取得用户同意</li> |
|
||||
/// 以上三个值,任何一个为false都会造成地图插件不工作(白屏情况) |
|
||||
/// |
|
||||
/// 高德SDK合规使用方案请参考:https://lbs.amap.com/news/sdkhgsy |
|
||||
/// |
|
||||
/// [AssertionError] will be thrown if [initialCameraPosition] is null; |
|
||||
const AMapWidget({ |
|
||||
Key? key, |
|
||||
this.privacyStatement, |
|
||||
this.apiKey, |
|
||||
this.initialCameraPosition = const CameraPosition( |
|
||||
target: LatLng(39.909187, 116.397451), |
|
||||
zoom: 10, |
|
||||
), |
|
||||
this.mapType = MapType.normal, |
|
||||
this.buildingsEnabled = true, |
|
||||
this.compassEnabled = false, |
|
||||
this.labelsEnabled = true, |
|
||||
this.limitBounds, |
|
||||
this.minMaxZoomPreference, |
|
||||
this.rotateGesturesEnabled = true, |
|
||||
this.scaleEnabled = true, |
|
||||
this.scrollGesturesEnabled = true, |
|
||||
this.tiltGesturesEnabled = true, |
|
||||
this.touchPoiEnabled = true, |
|
||||
this.trafficEnabled = false, |
|
||||
this.zoomGesturesEnabled = true, |
|
||||
this.onMapCreated, |
|
||||
this.gestureRecognizers = const <Factory<OneSequenceGestureRecognizer>>{}, |
|
||||
this.customStyleOptions, |
|
||||
this.myLocationStyleOptions, |
|
||||
this.onCameraMove, |
|
||||
this.onCameraMoveEnd, |
|
||||
this.onLocationChanged, |
|
||||
this.onTap, |
|
||||
this.onLongPress, |
|
||||
this.onPoiTouched, |
|
||||
this.markers = const <Marker>{}, |
|
||||
this.polylines = const <Polyline>{}, |
|
||||
this.polygons = const <Polygon>{}, |
|
||||
}) : super(key: key); |
|
||||
|
|
||||
/// |
|
||||
@override |
|
||||
State<StatefulWidget> createState() => _MapState(); |
|
||||
} |
|
||||
|
|
||||
class _MapState extends State<AMapWidget> { |
|
||||
Map<String, Marker> _markers = <String, Marker>{}; |
|
||||
Map<String, Polyline> _polylines = <String, Polyline>{}; |
|
||||
Map<String, Polygon> _polygons = <String, Polygon>{}; |
|
||||
|
|
||||
final Completer<AMapController> _controller = Completer<AMapController>(); |
|
||||
late _AMapOptions _mapOptions; |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
AMapUtil.init(context); |
|
||||
final Map<String, dynamic> creationParams = <String, dynamic>{ |
|
||||
'privacyStatement': widget.privacyStatement?.toMap(), |
|
||||
'apiKey': widget.apiKey?.toMap(), |
|
||||
'initialCameraPosition': widget.initialCameraPosition.toMap(), |
|
||||
'options': _mapOptions.toMap(), |
|
||||
'markersToAdd': serializeOverlaySet(widget.markers), |
|
||||
'polylinesToAdd': serializeOverlaySet(widget.polylines), |
|
||||
'polygonsToAdd': serializeOverlaySet(widget.polygons), |
|
||||
}; |
|
||||
Widget mapView = _methodChannel.buildView( |
|
||||
creationParams, |
|
||||
widget.gestureRecognizers, |
|
||||
onPlatformViewCreated, |
|
||||
); |
|
||||
return mapView; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
_mapOptions = _AMapOptions.fromWidget(widget); |
|
||||
_markers = keyByMarkerId(widget.markers); |
|
||||
_polygons = keyByPolygonId(widget.polygons); |
|
||||
_polylines = keyByPolylineId(widget.polylines); |
|
||||
print('initState AMapWidget'); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() async { |
|
||||
super.dispose(); |
|
||||
AMapController controller = await _controller.future; |
|
||||
controller.disponse(); |
|
||||
print('dispose AMapWidget with mapId: ${controller.mapId}'); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void reassemble() { |
|
||||
super.reassemble(); |
|
||||
print('reassemble AMapWidget'); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void deactivate() async { |
|
||||
super.deactivate(); |
|
||||
print('deactivate AMapWidget}'); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void didUpdateWidget(covariant AMapWidget oldWidget) { |
|
||||
super.didUpdateWidget(oldWidget); |
|
||||
_updateOptions(); |
|
||||
_updateMarkers(); |
|
||||
_updatePolylines(); |
|
||||
_updatePolygons(); |
|
||||
} |
|
||||
|
|
||||
Future<void> onPlatformViewCreated(int id) async { |
|
||||
final AMapController controller = await AMapController.init( |
|
||||
id, |
|
||||
widget.initialCameraPosition, |
|
||||
this, |
|
||||
); |
|
||||
_controller.complete(controller); |
|
||||
final MapCreatedCallback? _onMapCreated = widget.onMapCreated; |
|
||||
if (_onMapCreated != null) { |
|
||||
_onMapCreated(controller); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onMarkerTap(String markerId) { |
|
||||
final Marker? _marker = _markers[markerId]; |
|
||||
if (_marker != null) { |
|
||||
final ArgumentCallback<String>? _onTap = _marker.onTap; |
|
||||
if (_onTap != null) { |
|
||||
_onTap(markerId); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onMarkerDragEnd(String markerId, LatLng position) { |
|
||||
final Marker? _marker = _markers[markerId]; |
|
||||
if (_marker != null) { |
|
||||
final MarkerDragEndCallback? _onDragEnd = _marker.onDragEnd; |
|
||||
if (_onDragEnd != null) { |
|
||||
_onDragEnd(markerId, position); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onPolylineTap(String polylineId) { |
|
||||
final Polyline? _polyline = _polylines[polylineId]; |
|
||||
if (_polyline != null) { |
|
||||
final ArgumentCallback<String>? _onTap = _polyline.onTap; |
|
||||
if (_onTap != null) { |
|
||||
_onTap(polylineId); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _updateOptions() async { |
|
||||
final _AMapOptions newOptions = _AMapOptions.fromWidget(widget); |
|
||||
final Map<String, dynamic> updates = _mapOptions._updatesMap(newOptions); |
|
||||
if (updates.isEmpty) { |
|
||||
return; |
|
||||
} |
|
||||
final AMapController controller = await _controller.future; |
|
||||
// ignore: unawaited_futures |
|
||||
controller._updateMapOptions(updates); |
|
||||
_mapOptions = newOptions; |
|
||||
} |
|
||||
|
|
||||
void _updateMarkers() async { |
|
||||
final AMapController controller = await _controller.future; |
|
||||
// ignore: unawaited_futures |
|
||||
controller._updateMarkers( |
|
||||
MarkerUpdates.from(_markers.values.toSet(), widget.markers), |
|
||||
); |
|
||||
_markers = keyByMarkerId(widget.markers); |
|
||||
} |
|
||||
|
|
||||
void _updatePolylines() async { |
|
||||
final AMapController controller = await _controller.future; |
|
||||
controller._updatePolylines( |
|
||||
PolylineUpdates.from(_polylines.values.toSet(), widget.polylines), |
|
||||
); |
|
||||
_polylines = keyByPolylineId(widget.polylines); |
|
||||
} |
|
||||
|
|
||||
void _updatePolygons() async { |
|
||||
final AMapController controller = await _controller.future; |
|
||||
controller._updatePolygons( |
|
||||
PolygonUpdates.from(_polygons.values.toSet(), widget.polygons), |
|
||||
); |
|
||||
_polygons = keyByPolygonId(widget.polygons); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//高德地图参数设置 |
|
||||
class _AMapOptions { |
|
||||
///地图类型 |
|
||||
final MapType mapType; |
|
||||
|
|
||||
///自定义地图样式 |
|
||||
final CustomStyleOptions? customStyleOptions; |
|
||||
|
|
||||
///定位小蓝点 |
|
||||
final MyLocationStyleOptions? myLocationStyleOptions; |
|
||||
|
|
||||
//缩放级别范围 |
|
||||
final MinMaxZoomPreference? minMaxZoomPreference; |
|
||||
|
|
||||
///地图显示范围 |
|
||||
final LatLngBounds? limitBounds; |
|
||||
|
|
||||
///显示路况开关 |
|
||||
final bool? trafficEnabled; |
|
||||
|
|
||||
/// 地图poi是否允许点击 |
|
||||
final bool? touchPoiEnabled; |
|
||||
|
|
||||
///是否显示3D建筑物 |
|
||||
final bool? buildingsEnabled; |
|
||||
|
|
||||
///是否显示底图文字标注 |
|
||||
final bool? labelsEnabled; |
|
||||
|
|
||||
///是否显示指南针 |
|
||||
final bool? compassEnabled; |
|
||||
|
|
||||
///是否显示比例尺 |
|
||||
final bool? scaleEnabled; |
|
||||
|
|
||||
///是否支持缩放手势 |
|
||||
final bool? zoomGesturesEnabled; |
|
||||
|
|
||||
///是否支持滑动手势 |
|
||||
final bool? scrollGesturesEnabled; |
|
||||
|
|
||||
///是否支持旋转手势 |
|
||||
final bool? rotateGesturesEnabled; |
|
||||
|
|
||||
///是否支持仰角手势 |
|
||||
final bool? tiltGesturesEnabled; |
|
||||
|
|
||||
_AMapOptions({ |
|
||||
this.mapType = MapType.normal, |
|
||||
this.buildingsEnabled, |
|
||||
this.customStyleOptions, |
|
||||
this.myLocationStyleOptions, |
|
||||
this.compassEnabled, |
|
||||
this.labelsEnabled, |
|
||||
this.limitBounds, |
|
||||
this.minMaxZoomPreference, |
|
||||
this.scaleEnabled, |
|
||||
this.touchPoiEnabled, |
|
||||
this.trafficEnabled, |
|
||||
this.rotateGesturesEnabled, |
|
||||
this.scrollGesturesEnabled, |
|
||||
this.tiltGesturesEnabled, |
|
||||
this.zoomGesturesEnabled, |
|
||||
}); |
|
||||
|
|
||||
static _AMapOptions fromWidget(AMapWidget map) { |
|
||||
return _AMapOptions( |
|
||||
mapType: map.mapType, |
|
||||
buildingsEnabled: map.buildingsEnabled, |
|
||||
compassEnabled: map.compassEnabled, |
|
||||
labelsEnabled: map.labelsEnabled, |
|
||||
limitBounds: map.limitBounds, |
|
||||
minMaxZoomPreference: map.minMaxZoomPreference, |
|
||||
scaleEnabled: map.scaleEnabled, |
|
||||
touchPoiEnabled: map.touchPoiEnabled, |
|
||||
trafficEnabled: map.trafficEnabled, |
|
||||
rotateGesturesEnabled: map.rotateGesturesEnabled, |
|
||||
scrollGesturesEnabled: map.scrollGesturesEnabled, |
|
||||
tiltGesturesEnabled: map.tiltGesturesEnabled, |
|
||||
zoomGesturesEnabled: map.zoomGesturesEnabled, |
|
||||
customStyleOptions: map.customStyleOptions?.clone(), |
|
||||
myLocationStyleOptions: map.myLocationStyleOptions?.clone(), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> optionsMap = <String, dynamic>{}; |
|
||||
void addIfNonNull(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
optionsMap[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfNonNull('mapType', mapType.index); |
|
||||
addIfNonNull('buildingsEnabled', buildingsEnabled); |
|
||||
addIfNonNull('customStyleOptions', customStyleOptions?.clone().toMap()); |
|
||||
addIfNonNull('compassEnabled', compassEnabled); |
|
||||
addIfNonNull('labelsEnabled', labelsEnabled); |
|
||||
addIfNonNull('limitBounds', limitBounds?.toJson()); |
|
||||
addIfNonNull('minMaxZoomPreference', minMaxZoomPreference?.toJson()); |
|
||||
addIfNonNull('scaleEnabled', scaleEnabled); |
|
||||
addIfNonNull('touchPoiEnabled', touchPoiEnabled); |
|
||||
addIfNonNull('trafficEnabled', trafficEnabled); |
|
||||
addIfNonNull('rotateGesturesEnabled', rotateGesturesEnabled); |
|
||||
addIfNonNull('scrollGesturesEnabled', scrollGesturesEnabled); |
|
||||
addIfNonNull('tiltGesturesEnabled', tiltGesturesEnabled); |
|
||||
addIfNonNull('zoomGesturesEnabled', zoomGesturesEnabled); |
|
||||
addIfNonNull('myLocationStyle', myLocationStyleOptions?.clone().toMap()); |
|
||||
return optionsMap; |
|
||||
} |
|
||||
|
|
||||
Map<String, dynamic> _updatesMap(_AMapOptions newOptions) { |
|
||||
final Map<String, dynamic> prevOptionsMap = toMap(); |
|
||||
|
|
||||
return newOptions.toMap()..removeWhere( |
|
||||
(String key, dynamic value) => |
|
||||
(_checkChange(key, prevOptionsMap[key], value)), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
bool _checkChange(String key, dynamic preValue, dynamic newValue) { |
|
||||
if (key == 'myLocationStyle' || key == 'customStyleOptions') { |
|
||||
return preValue?.toString() == newValue?.toString(); |
|
||||
} else { |
|
||||
return preValue == newValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,41 +0,0 @@ |
|||||
import 'package:amap_flutter_map/src/core/method_channel_amap_flutter_map.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:flutter/foundation.dart'; |
|
||||
import 'package:flutter/gestures.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart'; |
|
||||
|
|
||||
/// “amap_flutter_map”平台特定实现必须扩展的接口 |
|
||||
abstract class AMapFlutterPlatform extends PlatformInterface { |
|
||||
static final Object _token = Object(); |
|
||||
AMapFlutterPlatform() : super(token: _token); |
|
||||
static AMapFlutterPlatform _instance = MethodChannelAMapFlutterMap() as AMapFlutterPlatform; |
|
||||
|
|
||||
/// The default instance of [AMapFlutterPlatform] to use. |
|
||||
/// |
|
||||
/// Defaults to [MethodChannelAMapFlutterMap]. |
|
||||
static AMapFlutterPlatform get instance => _instance; |
|
||||
|
|
||||
static set instance(AMapFlutterPlatform instance) { |
|
||||
PlatformInterface.verifyToken(instance, _token); |
|
||||
_instance = instance; |
|
||||
} |
|
||||
|
|
||||
/// /// Initializes the platform interface with [id]. |
|
||||
/// |
|
||||
/// This method is called when the plugin is first initialized. |
|
||||
Future<void> init(int id) { |
|
||||
throw UnimplementedError('init() has not been implemented.'); |
|
||||
} |
|
||||
|
|
||||
void dispose({required int id}) { |
|
||||
throw UnimplementedError('dispose() has not been implemented.'); |
|
||||
} |
|
||||
|
|
||||
Widget buildView( |
|
||||
Map<String, dynamic> creationParams, |
|
||||
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, |
|
||||
PlatformViewCreatedCallback onPlatformViewCreated) { |
|
||||
throw UnimplementedError('buildView() has not been implemented.'); |
|
||||
} |
|
||||
} |
|
||||
@ -1,77 +0,0 @@ |
|||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'package:amap_flutter_map/amap_flutter_map.dart'; |
|
||||
|
|
||||
///地图事件处理 |
|
||||
class MapEvent<T> { |
|
||||
/// 地图id |
|
||||
final int mapId; |
|
||||
|
|
||||
///返回的内容,对应的[MethodCall]中的[[arguments]] |
|
||||
final T value; |
|
||||
|
|
||||
/// 构造一个event |
|
||||
/// |
|
||||
/// `mapId` 当前地图的id |
|
||||
/// `value` 需要传输的值,可以为`null`. |
|
||||
MapEvent(this.mapId, this.value); |
|
||||
} |
|
||||
|
|
||||
///定位回调接口 |
|
||||
class LocationChangedEvent extends MapEvent<AMapLocation> { |
|
||||
LocationChangedEvent(int mapId, AMapLocation value) : super(mapId, value); |
|
||||
} |
|
||||
|
|
||||
///地图移动回调 |
|
||||
class CameraPositionMoveEvent extends MapEvent<CameraPosition> { |
|
||||
CameraPositionMoveEvent(int mapId, CameraPosition value) |
|
||||
: super(mapId, value); |
|
||||
} |
|
||||
|
|
||||
///地图移动结束回调 |
|
||||
class CameraPositionMoveEndEvent extends MapEvent<CameraPosition> { |
|
||||
CameraPositionMoveEndEvent(int mapId, CameraPosition value) |
|
||||
: super(mapId, value); |
|
||||
} |
|
||||
|
|
||||
///点击地图回调 |
|
||||
class MapTapEvent extends MapEvent<LatLng> { |
|
||||
MapTapEvent(int mapId, LatLng value) : super(mapId, value); |
|
||||
} |
|
||||
|
|
||||
///长按地图回调 |
|
||||
class MapLongPressEvent extends MapEvent<LatLng> { |
|
||||
MapLongPressEvent(int mapId, LatLng value) : super(mapId, value); |
|
||||
} |
|
||||
|
|
||||
/// 带位置回调的地图事件 |
|
||||
class _PositionedMapEvent<T> extends MapEvent<T> { |
|
||||
/// 事件中带的位置信息 |
|
||||
final LatLng position; |
|
||||
|
|
||||
/// 构造一个带位置的地图事件, |
|
||||
/// |
|
||||
/// `mapId` 当前地图的id |
|
||||
/// `value` 需要传输的值,可以为`null`. |
|
||||
_PositionedMapEvent(int mapId, this.position, T value) : super(mapId, value); |
|
||||
} |
|
||||
|
|
||||
/// [Marker] 的点击事件 |
|
||||
class MarkerTapEvent extends MapEvent<String> { |
|
||||
MarkerTapEvent(int mapId, String markerId) : super(mapId, markerId); |
|
||||
} |
|
||||
|
|
||||
/// [Marker] 的拖拽结束事件,附带拖拽结束时的位置信息[LatLng]. |
|
||||
class MarkerDragEndEvent extends _PositionedMapEvent<String> { |
|
||||
MarkerDragEndEvent(int mapId, LatLng position, String markerId) |
|
||||
: super(mapId, position, markerId); |
|
||||
} |
|
||||
|
|
||||
/// [Polyline] 的点击事件 |
|
||||
class PolylineTapEvent extends MapEvent<String> { |
|
||||
PolylineTapEvent(int mapId, String polylineId) : super(mapId, polylineId); |
|
||||
} |
|
||||
|
|
||||
/// Poi点击事件 |
|
||||
class MapPoiTouchEvent extends MapEvent<AMapPoi> { |
|
||||
MapPoiTouchEvent(int mapId, AMapPoi poi) : super(mapId, poi); |
|
||||
} |
|
||||
@ -1,274 +0,0 @@ |
|||||
import 'dart:async'; |
|
||||
import 'dart:typed_data'; |
|
||||
|
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'package:amap_flutter_map/src/core/amap_flutter_platform.dart'; |
|
||||
import 'package:amap_flutter_map/src/types/types.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:flutter/foundation.dart'; |
|
||||
import 'package:flutter/gestures.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'package:stream_transform/stream_transform.dart'; |
|
||||
|
|
||||
import 'map_event.dart'; |
|
||||
|
|
||||
const VIEW_TYPE = 'com.amap.flutter.map'; |
|
||||
|
|
||||
/// 使用[MethodChannel]与Native代码通信的[AMapFlutterPlatform]的实现。 |
|
||||
class MethodChannelAMapFlutterMap implements AMapFlutterPlatform { |
|
||||
final Map<int, MethodChannel> _channels = {}; |
|
||||
|
|
||||
MethodChannel channel(int mapId) { |
|
||||
return _channels[mapId]!; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Future<void> init(int mapId) { |
|
||||
MethodChannel? channel = _channels[mapId]; |
|
||||
if (channel == null) { |
|
||||
channel = MethodChannel('amap_flutter_map_$mapId'); |
|
||||
channel.setMethodCallHandler((call) => _handleMethodCall(call, mapId)); |
|
||||
_channels[mapId] = channel; |
|
||||
} |
|
||||
return channel.invokeMethod<void>('map#waitForMap'); |
|
||||
} |
|
||||
|
|
||||
///更新地图参数 |
|
||||
Future<void> updateMapOptions( |
|
||||
Map<String, dynamic> newOptions, { |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<void>( |
|
||||
'map#update', |
|
||||
<String, dynamic>{ |
|
||||
'options': newOptions, |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
/// 更新Marker的数据 |
|
||||
Future<void> updateMarkers( |
|
||||
MarkerUpdates markerUpdates, { |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<void>( |
|
||||
'markers#update', |
|
||||
markerUpdates.toMap(), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
/// 更新polyline的数据 |
|
||||
Future<void> updatePolylines( |
|
||||
PolylineUpdates polylineUpdates, { |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<void>( |
|
||||
'polylines#update', |
|
||||
polylineUpdates.toMap(), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
/// 更新polygon的数据 |
|
||||
Future<void> updatePolygons( |
|
||||
PolygonUpdates polygonUpdates, { |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<void>( |
|
||||
'polygons#update', |
|
||||
polygonUpdates.toMap(), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose({required int id}) { |
|
||||
if (_channels.containsKey(id)) { |
|
||||
_channels.remove(id); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget buildView( |
|
||||
Map<String, dynamic> creationParams, |
|
||||
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers, |
|
||||
void Function(int id) onPlatformViewCreated) { |
|
||||
if (defaultTargetPlatform == TargetPlatform.android) { |
|
||||
creationParams['debugMode'] = kDebugMode; |
|
||||
return AndroidView( |
|
||||
viewType: VIEW_TYPE, |
|
||||
onPlatformViewCreated: onPlatformViewCreated, |
|
||||
gestureRecognizers: gestureRecognizers, |
|
||||
creationParams: creationParams, |
|
||||
creationParamsCodec: const StandardMessageCodec(), |
|
||||
); |
|
||||
} else if (defaultTargetPlatform == TargetPlatform.iOS) { |
|
||||
return UiKitView( |
|
||||
viewType: VIEW_TYPE, |
|
||||
onPlatformViewCreated: onPlatformViewCreated, |
|
||||
gestureRecognizers: gestureRecognizers, |
|
||||
creationParams: creationParams, |
|
||||
creationParamsCodec: const StandardMessageCodec(), |
|
||||
); |
|
||||
} |
|
||||
return Text('当前平台:$defaultTargetPlatform, 不支持使用高德地图插件'); |
|
||||
} |
|
||||
|
|
||||
// handleMethodCall的`broadcast` |
|
||||
final StreamController<MapEvent> _mapEventStreamController = |
|
||||
StreamController<MapEvent>.broadcast(); |
|
||||
|
|
||||
// 根据mapid返回相应的event. |
|
||||
Stream<MapEvent> _events(int mapId) => |
|
||||
_mapEventStreamController.stream.where((event) => event.mapId == mapId); |
|
||||
|
|
||||
//定位回调 |
|
||||
Stream<LocationChangedEvent> onLocationChanged({required int mapId}) { |
|
||||
return _events(mapId).whereType<LocationChangedEvent>(); |
|
||||
} |
|
||||
|
|
||||
//Camera 移动回调 |
|
||||
Stream<CameraPositionMoveEvent> onCameraMove({required int mapId}) { |
|
||||
return _events(mapId).whereType<CameraPositionMoveEvent>(); |
|
||||
} |
|
||||
|
|
||||
///Camera 移动结束回调 |
|
||||
Stream<CameraPositionMoveEndEvent> onCameraMoveEnd({required int mapId}) { |
|
||||
return _events(mapId).whereType<CameraPositionMoveEndEvent>(); |
|
||||
} |
|
||||
|
|
||||
Stream<MapTapEvent> onMapTap({required int mapId}) { |
|
||||
return _events(mapId).whereType<MapTapEvent>(); |
|
||||
} |
|
||||
|
|
||||
Stream<MapLongPressEvent> onMapLongPress({required int mapId}) { |
|
||||
return _events(mapId).whereType<MapLongPressEvent>(); |
|
||||
} |
|
||||
|
|
||||
Stream<MapPoiTouchEvent> onPoiTouched({required int mapId}) { |
|
||||
return _events(mapId).whereType<MapPoiTouchEvent>(); |
|
||||
} |
|
||||
|
|
||||
Stream<MarkerTapEvent> onMarkerTap({required int mapId}) { |
|
||||
return _events(mapId).whereType<MarkerTapEvent>(); |
|
||||
} |
|
||||
|
|
||||
Stream<MarkerDragEndEvent> onMarkerDragEnd({required int mapId}) { |
|
||||
return _events(mapId).whereType<MarkerDragEndEvent>(); |
|
||||
} |
|
||||
|
|
||||
Stream<PolylineTapEvent> onPolylineTap({required int mapId}) { |
|
||||
return _events(mapId).whereType<PolylineTapEvent>(); |
|
||||
} |
|
||||
|
|
||||
Future<dynamic> _handleMethodCall(MethodCall call, int mapId) async { |
|
||||
switch (call.method) { |
|
||||
case 'location#changed': |
|
||||
try { |
|
||||
_mapEventStreamController.add(LocationChangedEvent( |
|
||||
mapId, AMapLocation.fromMap(call.arguments['location'])!)); |
|
||||
} catch (e) { |
|
||||
print("location#changed error=======>" + e.toString()); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case 'camera#onMove': |
|
||||
try { |
|
||||
_mapEventStreamController.add(CameraPositionMoveEvent( |
|
||||
mapId, CameraPosition.fromMap(call.arguments['position'])!)); |
|
||||
} catch (e) { |
|
||||
print("camera#onMove error===>" + e.toString()); |
|
||||
} |
|
||||
break; |
|
||||
case 'camera#onMoveEnd': |
|
||||
try { |
|
||||
_mapEventStreamController.add(CameraPositionMoveEndEvent( |
|
||||
mapId, CameraPosition.fromMap(call.arguments['position'])!)); |
|
||||
} catch (e) { |
|
||||
print("camera#onMoveEnd error===>" + e.toString()); |
|
||||
} |
|
||||
break; |
|
||||
case 'map#onTap': |
|
||||
_mapEventStreamController |
|
||||
.add(MapTapEvent(mapId, LatLng.fromJson(call.arguments['latLng'])!)); |
|
||||
break; |
|
||||
case 'map#onLongPress': |
|
||||
_mapEventStreamController.add(MapLongPressEvent( |
|
||||
mapId, LatLng.fromJson(call.arguments['latLng'])!)); |
|
||||
break; |
|
||||
|
|
||||
case 'marker#onTap': |
|
||||
_mapEventStreamController.add(MarkerTapEvent( |
|
||||
mapId, |
|
||||
call.arguments['markerId'], |
|
||||
)); |
|
||||
break; |
|
||||
case 'marker#onDragEnd': |
|
||||
_mapEventStreamController.add(MarkerDragEndEvent( |
|
||||
mapId, |
|
||||
LatLng.fromJson(call.arguments['position'])!, |
|
||||
call.arguments['markerId'])); |
|
||||
break; |
|
||||
case 'polyline#onTap': |
|
||||
_mapEventStreamController |
|
||||
.add(PolylineTapEvent(mapId, call.arguments['polylineId'])); |
|
||||
break; |
|
||||
case 'map#onPoiTouched': |
|
||||
try { |
|
||||
_mapEventStreamController.add( |
|
||||
MapPoiTouchEvent(mapId, AMapPoi.fromJson(call.arguments['poi'])!)); |
|
||||
} catch (e) { |
|
||||
print('map#onPoiTouched error===>' + e.toString()); |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
///移动镜头到一个新的位置 |
|
||||
Future<void> moveCamera( |
|
||||
CameraUpdate cameraUpdate, { |
|
||||
required int mapId, |
|
||||
bool animated = true, |
|
||||
int duration = 0, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<void>('camera#move', <String, dynamic>{ |
|
||||
'cameraUpdate': cameraUpdate.toJson(), |
|
||||
'animated': animated, |
|
||||
'duration': duration |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
///设置地图每秒渲染的帧数 |
|
||||
Future<void> setRenderFps(int fps, {required int mapId}) { |
|
||||
return channel(mapId) |
|
||||
.invokeMethod<void>('map#setRenderFps', <String, dynamic>{ |
|
||||
'fps': fps, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
///截屏 |
|
||||
Future<Uint8List?> takeSnapshot({ |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<Uint8List>('map#takeSnapshot'); |
|
||||
} |
|
||||
|
|
||||
//获取地图审图号(普通地图) |
|
||||
Future<String?> getMapContentApprovalNumber({ |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<String>('map#contentApprovalNumber'); |
|
||||
} |
|
||||
|
|
||||
//获取地图审图号(卫星地图) |
|
||||
Future<String?> getSatelliteImageApprovalNumber({ |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId) |
|
||||
.invokeMethod<String>('map#satelliteImageApprovalNumber'); |
|
||||
} |
|
||||
|
|
||||
Future<void> clearDisk({ |
|
||||
required int mapId, |
|
||||
}) { |
|
||||
return channel(mapId).invokeMethod<void>('map#clearDisk'); |
|
||||
} |
|
||||
} |
|
||||
@ -1,29 +0,0 @@ |
|||||
/// 地图覆盖物基类 |
|
||||
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(); |
|
||||
} |
|
||||
@ -1,140 +0,0 @@ |
|||||
// Copyright 2018 The Chromium Authors. All rights reserved. |
|
||||
// Use of this source code is governed by a BSD-style license that can be |
|
||||
// found in the LICENSE file. |
|
||||
|
|
||||
import 'dart:async' show Future; |
|
||||
import 'dart:typed_data' show Uint8List; |
|
||||
import 'dart:ui'; |
|
||||
|
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart' show AMapUtil; |
|
||||
import 'package:flutter/material.dart' |
|
||||
show ImageConfiguration, AssetImage, AssetBundleImageKey; |
|
||||
import 'package:flutter/services.dart' show AssetBundle; |
|
||||
|
|
||||
import 'package:flutter/foundation.dart' show kIsWeb; |
|
||||
|
|
||||
/// Bitmap工具类 |
|
||||
class BitmapDescriptor { |
|
||||
const BitmapDescriptor._(this._json); |
|
||||
|
|
||||
/// 红色. |
|
||||
static const double hueRed = 0.0; |
|
||||
|
|
||||
/// 橙色. |
|
||||
static const double hueOrange = 30.0; |
|
||||
|
|
||||
/// 黄色. |
|
||||
static const double hueYellow = 60.0; |
|
||||
|
|
||||
/// 绿色. |
|
||||
static const double hueGreen = 120.0; |
|
||||
|
|
||||
/// 青色. |
|
||||
static const double hueCyan = 180.0; |
|
||||
|
|
||||
/// 天蓝色. |
|
||||
static const double hueAzure = 210.0; |
|
||||
|
|
||||
/// 蓝色. |
|
||||
static const double hueBlue = 240.0; |
|
||||
|
|
||||
/// 紫色. |
|
||||
static const double hueViolet = 270.0; |
|
||||
|
|
||||
/// 酒红色. |
|
||||
static const double hueMagenta = 300.0; |
|
||||
|
|
||||
/// 玫瑰红. |
|
||||
static const double hueRose = 330.0; |
|
||||
|
|
||||
/// 创建默认的marker 图标的 bitmap 描述信息对象. |
|
||||
static const BitmapDescriptor defaultMarker = |
|
||||
BitmapDescriptor._(<dynamic>['defaultMarker']); |
|
||||
|
|
||||
/// 创建引用默认着色的BitmapDescriptor |
|
||||
static BitmapDescriptor defaultMarkerWithHue(double hue) { |
|
||||
assert(0.0 <= hue && hue < 360.0); |
|
||||
String filename = "BLUE.png"; |
|
||||
if (hue == hueRed) { |
|
||||
filename = "RED.png"; |
|
||||
} else if (hue == hueOrange) { |
|
||||
filename = "ORANGE.png"; |
|
||||
} else if (hue == hueYellow) { |
|
||||
filename = "YELLOW.png"; |
|
||||
} else if (hue == hueGreen) { |
|
||||
filename = "GREEN.png"; |
|
||||
} else if (hue == hueCyan) { |
|
||||
filename = "CYAN.png"; |
|
||||
} else if (hue == hueAzure) { |
|
||||
filename = "AZURE.png"; |
|
||||
} else if (hue == hueBlue) { |
|
||||
filename = "BLUE.png"; |
|
||||
} else if (hue == hueViolet) { |
|
||||
filename = "VIOLET.png"; |
|
||||
} else if (hue == hueMagenta) { |
|
||||
filename = "MAGENTA.png"; |
|
||||
} else if (hue == hueRose) { |
|
||||
filename = "ROSE.png"; |
|
||||
} |
|
||||
return BitmapDescriptor._(<dynamic>[ |
|
||||
'fromAssetImage', |
|
||||
"packages/amap_flutter_map/res/$filename", |
|
||||
AMapUtil.devicePixelRatio |
|
||||
]); |
|
||||
} |
|
||||
|
|
||||
///根据输入的icon路径[iconPath]创建[BitmapDescriptor] |
|
||||
static BitmapDescriptor fromIconPath(String iconPath) { |
|
||||
return BitmapDescriptor._(<dynamic>[ |
|
||||
'fromAsset', |
|
||||
iconPath, |
|
||||
]); |
|
||||
} |
|
||||
|
|
||||
///从资源图像创建[BitmapDescriptor]。 |
|
||||
/// |
|
||||
///Flutter中的assert的资产图像按以下方式存储: |
|
||||
/// https://flutter.dev/docs/development/ui/assets and images声明-分辨率感知图像资源 |
|
||||
///该方法考虑了各种资产解决方案 |
|
||||
///并根据dpi将图像缩放到正确的分辨率。 |
|
||||
///将`mipmaps1设置为false可加载图像的精确dpi版本,默认情况下,`mipmap`为true。 |
|
||||
static Future<BitmapDescriptor> fromAssetImage( |
|
||||
ImageConfiguration configuration, |
|
||||
String assetName, { |
|
||||
AssetBundle? bundle, |
|
||||
String? package, |
|
||||
bool mipmaps = true, |
|
||||
}) async { |
|
||||
if (!mipmaps && configuration.devicePixelRatio != null) { |
|
||||
return BitmapDescriptor._(<dynamic>[ |
|
||||
'fromAssetImage', |
|
||||
assetName, |
|
||||
configuration.devicePixelRatio, |
|
||||
]); |
|
||||
} |
|
||||
final AssetImage assetImage = |
|
||||
AssetImage(assetName, package: package, bundle: bundle); |
|
||||
final AssetBundleImageKey assetBundleImageKey = |
|
||||
await assetImage.obtainKey(configuration); |
|
||||
final Size? size = configuration.size; |
|
||||
return BitmapDescriptor._(<dynamic>[ |
|
||||
'fromAssetImage', |
|
||||
assetBundleImageKey.name, |
|
||||
assetBundleImageKey.scale, |
|
||||
if (kIsWeb && size != null) |
|
||||
[ |
|
||||
size.width, |
|
||||
size.height, |
|
||||
], |
|
||||
]); |
|
||||
} |
|
||||
|
|
||||
/// 根据将PNG图片转换后的二进制数据[byteData]创建BitmapDescriptor |
|
||||
static BitmapDescriptor fromBytes(Uint8List byteData) { |
|
||||
return BitmapDescriptor._(<dynamic>['fromBytes', byteData]); |
|
||||
} |
|
||||
|
|
||||
final dynamic _json; |
|
||||
|
|
||||
dynamic toMap() => _json; |
|
||||
} |
|
||||
@ -1,150 +0,0 @@ |
|||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
|
|
||||
/// 相机位置,包含可视区域的位置参数。 |
|
||||
class CameraPosition { |
|
||||
/// 构造一个CameraPosition 对象 |
|
||||
/// |
|
||||
/// 如果[bearing], [target], [tilt], 或者 [zoom] 为null时会返回[AssertionError] |
|
||||
const CameraPosition({ |
|
||||
this.bearing = 0.0, |
|
||||
required this.target, |
|
||||
this.tilt = 0.0, |
|
||||
this.zoom = 10, |
|
||||
}); |
|
||||
|
|
||||
/// 可视区域指向的方向,以角度为单位,从正北向逆时针方向计算,从0 度到360 度。 |
|
||||
final double bearing; |
|
||||
|
|
||||
/// 目标位置的屏幕中心点经纬度坐标。 |
|
||||
final LatLng target; |
|
||||
|
|
||||
/// 目标可视区域的倾斜度,以角度为单位。范围从0到360度 |
|
||||
final double tilt; |
|
||||
|
|
||||
/// 目标可视区域的缩放级别 |
|
||||
final double zoom; |
|
||||
|
|
||||
/// 将[CameraPosition]装换成Map |
|
||||
/// |
|
||||
/// 主要在插件内部使用 |
|
||||
dynamic toMap() => <String, dynamic>{ |
|
||||
'bearing': bearing, |
|
||||
'target': target.toJson(), |
|
||||
'tilt': tilt, |
|
||||
'zoom': zoom, |
|
||||
}; |
|
||||
|
|
||||
/// 从Map转换成[CameraPosition] |
|
||||
/// |
|
||||
/// 主要在插件内部使用 |
|
||||
static CameraPosition? fromMap(dynamic json) { |
|
||||
if (json == null || !(json is Map<dynamic, dynamic>)) { |
|
||||
return null; |
|
||||
} |
|
||||
final LatLng? target = LatLng.fromJson(json['target']); |
|
||||
if (target == null) { |
|
||||
return null; |
|
||||
} |
|
||||
return CameraPosition( |
|
||||
bearing: json['bearing'], |
|
||||
target: target, |
|
||||
tilt: json['tilt'], |
|
||||
zoom: json['zoom'], |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(dynamic other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (runtimeType != other.runtimeType) return false; |
|
||||
final CameraPosition typedOther = other; |
|
||||
return bearing == typedOther.bearing && |
|
||||
target == typedOther.target && |
|
||||
tilt == typedOther.tilt && |
|
||||
zoom == typedOther.zoom; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => Object.hash(bearing, target, tilt, zoom); |
|
||||
|
|
||||
@override |
|
||||
String toString() => |
|
||||
'CameraPosition(bearing: $bearing, target: $target, tilt: $tilt, zoom: $zoom)'; |
|
||||
} |
|
||||
|
|
||||
/// 描述地图状态将要发生的变化 |
|
||||
class CameraUpdate { |
|
||||
CameraUpdate._(this._json); |
|
||||
|
|
||||
///返回根据新的[CameraPosition]移动后的[CameraUpdate]. |
|
||||
/// |
|
||||
///主要用于改变地图的中心点、缩放级别、倾斜角、角度等信息 |
|
||||
static CameraUpdate newCameraPosition(CameraPosition cameraPosition) { |
|
||||
return CameraUpdate._(<dynamic>[ |
|
||||
'newCameraPosition', |
|
||||
cameraPosition.toMap(), |
|
||||
]); |
|
||||
} |
|
||||
|
|
||||
///移动到一个新的位置点[latLng] |
|
||||
/// |
|
||||
///主要用于改变地图的中心点 |
|
||||
static CameraUpdate newLatLng(LatLng latLng) { |
|
||||
return CameraUpdate._(<dynamic>['newLatLng', latLng.toJson()]); |
|
||||
} |
|
||||
|
|
||||
///根据指定到摄像头显示范围[bounds]和边界值[padding]创建一个CameraUpdate对象 |
|
||||
/// |
|
||||
///主要用于根据指定的显示范围[bounds]以最佳的视野显示地图 |
|
||||
static CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) { |
|
||||
return CameraUpdate._(<dynamic>[ |
|
||||
'newLatLngBounds', |
|
||||
bounds.toJson(), |
|
||||
padding, |
|
||||
]); |
|
||||
} |
|
||||
|
|
||||
/// 根据指定的新的位置[latLng]和缩放级别[zoom]创建一个CameraUpdate对象 |
|
||||
/// |
|
||||
/// 主要用于同时改变中心点和缩放级别 |
|
||||
static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) { |
|
||||
return CameraUpdate._(<dynamic>['newLatLngZoom', latLng.toJson(), zoom]); |
|
||||
} |
|
||||
|
|
||||
/// 按照指定到像素点[dx]和[dy]移动地图中心点 |
|
||||
/// |
|
||||
/// [dx]是水平移动的像素数。正值代表可视区域向右移动,负值代表可视区域向左移动 |
|
||||
/// |
|
||||
/// [dy]是垂直移动的像素数。正值代表可视区域向下移动,负值代表可视区域向上移动 |
|
||||
/// |
|
||||
/// 返回包含x,y方向上移动像素数的cameraUpdate对象。 |
|
||||
static CameraUpdate scrollBy(double dx, double dy) { |
|
||||
return CameraUpdate._(<dynamic>['scrollBy', dx, dy]); |
|
||||
} |
|
||||
|
|
||||
/// 创建一个在当前地图显示的级别基础上加1的CameraUpdate对象 |
|
||||
/// |
|
||||
///主要用于放大地图缩放级别,在当前地图显示的级别基础上加1 |
|
||||
static CameraUpdate zoomIn() { |
|
||||
return CameraUpdate._(<dynamic>['zoomIn']); |
|
||||
} |
|
||||
|
|
||||
/// 创建一个在当前地图显示的级别基础上加1的CameraUpdate对象 |
|
||||
/// |
|
||||
/// 主要用于减少地图缩放级别,在当前地图显示的级别基础上减1 |
|
||||
static CameraUpdate zoomOut() { |
|
||||
return CameraUpdate._(<dynamic>['zoomOut']); |
|
||||
} |
|
||||
|
|
||||
/// 创建一个指定缩放级别[zoom]的CameraUpdate对象 |
|
||||
/// |
|
||||
/// 主要用于设置地图缩放级别 |
|
||||
static CameraUpdate zoomTo(double zoom) { |
|
||||
return CameraUpdate._(<dynamic>['zoomTo', zoom]); |
|
||||
} |
|
||||
|
|
||||
final dynamic _json; |
|
||||
|
|
||||
dynamic toJson() => _json; |
|
||||
} |
|
||||
@ -1,249 +0,0 @@ |
|||||
// Copyright 2018 The Chromium Authors. All rights reserved. |
|
||||
// Use of this source code is governed by a BSD-style license that can be |
|
||||
// found in the LICENSE file. |
|
||||
|
|
||||
import 'dart:ui' show Offset; |
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'base_overlay.dart'; |
|
||||
import 'bitmap.dart'; |
|
||||
|
|
||||
/// Marker拖动回调 |
|
||||
typedef void MarkerDragEndCallback(String id, LatLng endPosition); |
|
||||
|
|
||||
///Marker的气泡 |
|
||||
/// |
|
||||
///Android和iOS的实现机制有差异,仅在接口层面拉齐,效果一致 |
|
||||
class InfoWindow { |
|
||||
/// 为 [Marker] 产生一个不可修改的文本气泡. |
|
||||
const InfoWindow({this.title, this.snippet}); |
|
||||
|
|
||||
/// 无文本的气泡 |
|
||||
static const InfoWindow noText = InfoWindow(); |
|
||||
|
|
||||
/// 气泡的title |
|
||||
final String? title; |
|
||||
|
|
||||
/// 气泡的详细信息 |
|
||||
final String? snippet; |
|
||||
|
|
||||
/// 气泡copy方法 |
|
||||
/// |
|
||||
InfoWindow copyWith({String? titleParam, String? snippetParam}) { |
|
||||
return InfoWindow( |
|
||||
title: titleParam ?? title, |
|
||||
snippet: snippetParam ?? snippet, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Map<String, dynamic> _toMap() { |
|
||||
final Map<String, dynamic> json = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfPresent(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
json[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfPresent('title', title); |
|
||||
addIfPresent('snippet', snippet); |
|
||||
return json; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is! InfoWindow) { |
|
||||
return false; |
|
||||
} |
|
||||
final InfoWindow typedOther = other; |
|
||||
return title == typedOther.title && snippet == typedOther.snippet; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => Object.hash(title, snippet); |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return 'InfoWindow{title: $title, snippet: $snippet}'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 点覆盖物的类 |
|
||||
class Marker extends BaseOverlay { |
|
||||
Marker({ |
|
||||
required this.position, |
|
||||
double alpha = 1.0, |
|
||||
Offset anchor = const Offset(0.5, 1.0), |
|
||||
this.clickable = true, |
|
||||
this.draggable = false, |
|
||||
this.icon = BitmapDescriptor.defaultMarker, |
|
||||
this.infoWindowEnable = true, |
|
||||
this.infoWindow = InfoWindow.noText, |
|
||||
this.rotation = 0.0, |
|
||||
this.visible = true, |
|
||||
this.zIndex = 0.0, |
|
||||
this.onTap, |
|
||||
this.onDragEnd, |
|
||||
}) : this.alpha = |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
(alpha != null ? (alpha < 0 ? 0 : (alpha > 1 ? 1 : alpha)) : alpha), |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
this.anchor = |
|
||||
(anchor == null |
|
||||
? Offset(0.5, 1.0) |
|
||||
: ((anchor.dx < 0 || |
|
||||
anchor.dx > 1 || |
|
||||
anchor.dy < 0 || |
|
||||
anchor.dy > 1) |
|
||||
? Offset(0.5, 1.0) |
|
||||
: anchor)), |
|
||||
super(); |
|
||||
|
|
||||
/// 透明度 |
|
||||
final double alpha; |
|
||||
|
|
||||
/// 覆盖物视图相对地图上的经纬度位置的锚点 |
|
||||
final Offset anchor; |
|
||||
|
|
||||
/// 是否可点击,默认为true |
|
||||
final bool clickable; |
|
||||
|
|
||||
/// 是否可拖拽,默认为false |
|
||||
final bool draggable; |
|
||||
|
|
||||
/// 覆盖物的图标 |
|
||||
final BitmapDescriptor icon; |
|
||||
|
|
||||
/// 是否显示气泡,如果为true,则点击[Marker]后,会显示该气泡[InfoWindow] |
|
||||
/// 如果为false,则始终不会显示该气泡 |
|
||||
final bool infoWindowEnable; |
|
||||
|
|
||||
/// 覆盖物上的气泡,当被点击时,如果infoWindowEnable为true,则会显示出来 |
|
||||
final InfoWindow infoWindow; |
|
||||
|
|
||||
/// 位置,不能为空 |
|
||||
final LatLng position; |
|
||||
|
|
||||
/// 旋转角度,以锚点为中心,顺时针旋转(单位:度数) |
|
||||
/// |
|
||||
/// 注意:iOS端目前仅支持绕marker中心点旋转 |
|
||||
final double rotation; |
|
||||
|
|
||||
/// 是否可见 |
|
||||
final bool visible; |
|
||||
|
|
||||
/// z轴的值,用于调整该覆盖物的相对绘制层级关系 |
|
||||
/// 值越小,图层越靠下,iOS该值不支持动态修改,仅能在初始化时指定 |
|
||||
final double zIndex; |
|
||||
|
|
||||
/// 回调的参数是对应的id |
|
||||
final ArgumentCallback<String>? onTap; |
|
||||
|
|
||||
/// Marker被拖拽结束的回调 |
|
||||
final MarkerDragEndCallback? onDragEnd; |
|
||||
|
|
||||
/// copy的真正复制的参数,主要用于需要修改某个属性参数时使用 |
|
||||
Marker copyWith({ |
|
||||
double? alphaParam, |
|
||||
Offset? anchorParam, |
|
||||
bool? clickableParam, |
|
||||
bool? draggableParam, |
|
||||
BitmapDescriptor? iconParam, |
|
||||
bool? infoWindowEnableParam, |
|
||||
InfoWindow? infoWindowParam, |
|
||||
LatLng? positionParam, |
|
||||
double? rotationParam, |
|
||||
bool? visibleParam, |
|
||||
ArgumentCallback<String?>? onTapParam, |
|
||||
MarkerDragEndCallback? onDragEndParam, |
|
||||
}) { |
|
||||
Marker copyMark = Marker( |
|
||||
alpha: alphaParam ?? alpha, |
|
||||
anchor: anchorParam ?? anchor, |
|
||||
clickable: clickableParam ?? clickable, |
|
||||
draggable: draggableParam ?? draggable, |
|
||||
icon: iconParam ?? icon, |
|
||||
infoWindowEnable: infoWindowEnableParam ?? infoWindowEnable, |
|
||||
infoWindow: infoWindowParam ?? infoWindow, |
|
||||
position: positionParam ?? position, |
|
||||
rotation: rotationParam ?? rotation, |
|
||||
visible: visibleParam ?? visible, |
|
||||
zIndex: zIndex, |
|
||||
onTap: onTapParam ?? onTap, |
|
||||
onDragEnd: onDragEndParam ?? onDragEnd, |
|
||||
); |
|
||||
copyMark.setIdForCopy(id); |
|
||||
return copyMark; |
|
||||
} |
|
||||
|
|
||||
Marker clone() => copyWith(); |
|
||||
|
|
||||
@override |
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> json = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfPresent(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
json[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfPresent('id', id); |
|
||||
addIfPresent('alpha', alpha); |
|
||||
addIfPresent('anchor', _offsetToJson(anchor)); |
|
||||
addIfPresent('clickable', clickable); |
|
||||
addIfPresent('draggable', draggable); |
|
||||
addIfPresent('icon', icon.toMap()); |
|
||||
addIfPresent('infoWindowEnable', infoWindowEnable); |
|
||||
addIfPresent('infoWindow', infoWindow._toMap()); |
|
||||
addIfPresent('position', position.toJson()); |
|
||||
addIfPresent('rotation', rotation); |
|
||||
addIfPresent('visible', visible); |
|
||||
addIfPresent('zIndex', zIndex); |
|
||||
return json; |
|
||||
} |
|
||||
|
|
||||
dynamic _offsetToJson(Offset offset) { |
|
||||
return <dynamic>[offset.dx, offset.dy]; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is! Marker) return false; |
|
||||
final Marker typedOther = other; |
|
||||
return id == typedOther.id && |
|
||||
alpha == typedOther.alpha && |
|
||||
anchor == typedOther.anchor && |
|
||||
clickable == typedOther.clickable && |
|
||||
draggable == typedOther.draggable && |
|
||||
icon == typedOther.icon && |
|
||||
infoWindowEnable == typedOther.infoWindowEnable && |
|
||||
infoWindow == typedOther.infoWindow && |
|
||||
position == typedOther.position && |
|
||||
rotation == typedOther.rotation && |
|
||||
visible == typedOther.visible && |
|
||||
zIndex == typedOther.zIndex; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => super.hashCode; |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return 'Marker{id: $id, alpha: $alpha, anchor: $anchor, ' |
|
||||
'clickable: $clickable, draggable: $draggable,' |
|
||||
'icon: $icon, infoWindowEnable: $infoWindowEnable, infoWindow: $infoWindow, position: $position, rotation: $rotation, ' |
|
||||
'visible: $visible, zIndex: $zIndex, onTap: $onTap}'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Map<String, Marker> keyByMarkerId(Iterable<Marker> markers) { |
|
||||
return Map<String, Marker>.fromEntries( |
|
||||
markers.map( |
|
||||
(Marker marker) => MapEntry<String, Marker>(marker.id, marker.clone()), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
@ -1,105 +0,0 @@ |
|||||
import 'dart:ui' show hashValues; |
|
||||
|
|
||||
import 'package:flutter/foundation.dart' show setEquals; |
|
||||
|
|
||||
import 'types.dart'; |
|
||||
import 'marker.dart'; |
|
||||
|
|
||||
/// 用以描述Marker的更新项 |
|
||||
class MarkerUpdates { |
|
||||
/// 根据之前的marker列表[previous]和当前的marker列表[current]创建[MakerUpdates]. |
|
||||
MarkerUpdates.from(Set<Marker> previous, Set<Marker> current) { |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (previous == null) { |
|
||||
previous = Set<Marker>.identity(); |
|
||||
} |
|
||||
|
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (current == null) { |
|
||||
current = Set<Marker>.identity(); |
|
||||
} |
|
||||
|
|
||||
final Map<String, Marker> previousMarkers = keyByMarkerId(previous); |
|
||||
final Map<String, Marker> currentMarkers = keyByMarkerId(current); |
|
||||
|
|
||||
final Set<String> prevMarkerIds = previousMarkers.keys.toSet(); |
|
||||
final Set<String> currentMarkerIds = currentMarkers.keys.toSet(); |
|
||||
|
|
||||
Marker idToCurrentMarker(String id) { |
|
||||
return currentMarkers[id]!; |
|
||||
} |
|
||||
|
|
||||
final Set<String> _markerIdsToRemove = prevMarkerIds.difference( |
|
||||
currentMarkerIds, |
|
||||
); |
|
||||
|
|
||||
final Set<Marker> _markersToAdd = |
|
||||
currentMarkerIds |
|
||||
.difference(prevMarkerIds) |
|
||||
.map(idToCurrentMarker) |
|
||||
.toSet(); |
|
||||
|
|
||||
bool hasChanged(Marker current) { |
|
||||
final Marker? previous = previousMarkers[current.id]; |
|
||||
return current != previous; |
|
||||
} |
|
||||
|
|
||||
final Set<Marker> _markersToChange = |
|
||||
currentMarkerIds |
|
||||
.intersection(prevMarkerIds) |
|
||||
.map(idToCurrentMarker) |
|
||||
.where(hasChanged) |
|
||||
.toSet(); |
|
||||
|
|
||||
markersToAdd = _markersToAdd; |
|
||||
markerIdsToRemove = _markerIdsToRemove; |
|
||||
markersToChange = _markersToChange; |
|
||||
} |
|
||||
|
|
||||
/// 想要添加的marker集合. |
|
||||
Set<Marker>? markersToAdd; |
|
||||
|
|
||||
/// 想要删除的marker的id集合 |
|
||||
Set<String>? markerIdsToRemove; |
|
||||
|
|
||||
/// 想要更新的marker集合. |
|
||||
Set<Marker>? markersToChange; |
|
||||
|
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> updateMap = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfNonNull(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
updateMap[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfNonNull('markersToAdd', serializeOverlaySet(markersToAdd!)); |
|
||||
addIfNonNull('markersToChange', serializeOverlaySet(markersToChange!)); |
|
||||
addIfNonNull('markerIdsToRemove', markerIdsToRemove?.toList()); |
|
||||
|
|
||||
return updateMap; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is! MarkerUpdates) return false; |
|
||||
final MarkerUpdates typedOther = other; |
|
||||
return setEquals(markersToAdd, typedOther.markersToAdd) && |
|
||||
setEquals(markerIdsToRemove, typedOther.markerIdsToRemove) && |
|
||||
setEquals(markersToChange, typedOther.markersToChange); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => |
|
||||
Object.hash(markersToAdd, markerIdsToRemove, markersToChange); |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return '_MarkerUpdates{markersToAdd: $markersToAdd, ' |
|
||||
'markerIdsToRemove: $markerIdsToRemove, ' |
|
||||
'markersToChange: $markersToChange}'; |
|
||||
} |
|
||||
} |
|
||||
@ -1,120 +0,0 @@ |
|||||
// Copyright 2019 The Chromium Authors. All rights reserved. |
|
||||
// Use of this source code is governed by a BSD-style license that can be |
|
||||
// found in the LICENSE file. |
|
||||
|
|
||||
import 'package:flutter/foundation.dart' show listEquals; |
|
||||
import 'package:flutter/material.dart' show Color; |
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'base_overlay.dart'; |
|
||||
import 'polyline.dart'; |
|
||||
|
|
||||
/// 线相关的覆盖物类,内部的属性,描述了覆盖物的纹理、颜色、线宽等特征 |
|
||||
class Polygon extends BaseOverlay { |
|
||||
/// 默认构造函数 |
|
||||
Polygon( |
|
||||
{required this.points, |
|
||||
double strokeWidth = 10, |
|
||||
this.strokeColor = const Color(0xCC00BFFF), |
|
||||
this.fillColor = const Color(0xC487CEFA), |
|
||||
this.visible = true, |
|
||||
this.joinType = JoinType.bevel}) |
|
||||
: assert(points.length > 0), |
|
||||
this.strokeWidth = (strokeWidth <= 0 ? 10 : strokeWidth), |
|
||||
super(); |
|
||||
|
|
||||
/// 覆盖物的坐标点数组,不能为空 |
|
||||
final List<LatLng> points; |
|
||||
|
|
||||
/// 边框宽度,单位为逻辑像素,同Android中的dp,iOS中的point |
|
||||
final double strokeWidth; |
|
||||
|
|
||||
/// 边框颜色,默认值为(0xCCC4E0F0) |
|
||||
final Color strokeColor; |
|
||||
|
|
||||
/// 填充颜色,默认值为(0xC4E0F0CC) |
|
||||
final Color fillColor; |
|
||||
|
|
||||
/// 是否可见 |
|
||||
final bool visible; |
|
||||
|
|
||||
/// 连接点类型,该参数不支持copy时修改,仅能在初始化时设置一次 |
|
||||
final JoinType joinType; |
|
||||
|
|
||||
/// 实际copy函数 |
|
||||
Polygon copyWith({ |
|
||||
List<LatLng>? pointsParam, |
|
||||
double? strokeWidthParam, |
|
||||
Color? strokeColorParam, |
|
||||
Color? fillColorParam, |
|
||||
bool? visibleParam, |
|
||||
}) { |
|
||||
Polygon copyPolyline = Polygon( |
|
||||
points: pointsParam ?? points, |
|
||||
strokeWidth: strokeWidthParam ?? strokeWidth, |
|
||||
strokeColor: strokeColorParam ?? strokeColor, |
|
||||
fillColor: fillColorParam ?? fillColor, |
|
||||
visible: visibleParam ?? visible, |
|
||||
joinType: joinType, |
|
||||
); |
|
||||
copyPolyline.setIdForCopy(id); |
|
||||
return copyPolyline; |
|
||||
} |
|
||||
|
|
||||
Polygon clone() => copyWith(); |
|
||||
|
|
||||
/// 转换成可以序列化的map |
|
||||
@override |
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> json = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfPresent(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
json[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfPresent('id', id); |
|
||||
json['points'] = _pointsToJson(); |
|
||||
addIfPresent('strokeWidth', strokeWidth); |
|
||||
addIfPresent('strokeColor', strokeColor.value); |
|
||||
addIfPresent('fillColor', fillColor.value); |
|
||||
addIfPresent('visible', visible); |
|
||||
addIfPresent('joinType', joinType.index); |
|
||||
return json; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is !Polygon) return false; |
|
||||
final Polygon typedOther = other; |
|
||||
return id == typedOther.id && |
|
||||
listEquals(points, typedOther.points) && |
|
||||
strokeWidth == typedOther.strokeWidth && |
|
||||
strokeColor == typedOther.strokeColor && |
|
||||
fillColor == typedOther.fillColor && |
|
||||
visible == typedOther.visible && |
|
||||
joinType == typedOther.joinType; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => super.hashCode; |
|
||||
|
|
||||
dynamic _pointsToJson() { |
|
||||
final List<dynamic> result = <dynamic>[]; |
|
||||
for (final LatLng point in points) { |
|
||||
result.add(point.toJson()); |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Map<String, Polygon> keyByPolygonId(Iterable<Polygon> polylines) { |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (polylines == null) { |
|
||||
return <String, Polygon>{}; |
|
||||
} |
|
||||
return Map<String, Polygon>.fromEntries(polylines.map((Polygon polyline) => |
|
||||
MapEntry<String, Polygon>(polyline.id, polyline.clone()))); |
|
||||
} |
|
||||
@ -1,108 +0,0 @@ |
|||||
// Copyright 2018 The Chromium Authors. All rights reserved. |
|
||||
// Use of this source code is governed by a BSD-style license that can be |
|
||||
// found in the LICENSE file. |
|
||||
|
|
||||
import 'dart:ui' show hashValues; |
|
||||
|
|
||||
import 'package:flutter/foundation.dart' show setEquals; |
|
||||
import 'types.dart'; |
|
||||
|
|
||||
/// 该类主要用以描述[Polygon]的增删改等更新操作 |
|
||||
class PolygonUpdates { |
|
||||
/// 通过Polygon的前后更新集合构造一个PolygonUpdates |
|
||||
PolygonUpdates.from(Set<Polygon> previous, Set<Polygon> current) { |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (previous == null) { |
|
||||
previous = Set<Polygon>.identity(); |
|
||||
} |
|
||||
|
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (current == null) { |
|
||||
current = Set<Polygon>.identity(); |
|
||||
} |
|
||||
|
|
||||
final Map<String, Polygon> previousPolygons = keyByPolygonId(previous); |
|
||||
final Map<String, Polygon> currentPolygons = keyByPolygonId(current); |
|
||||
|
|
||||
final Set<String> prevPolygonIds = previousPolygons.keys.toSet(); |
|
||||
final Set<String> currentPolygonIds = currentPolygons.keys.toSet(); |
|
||||
|
|
||||
Polygon idToCurrentPolygon(String id) { |
|
||||
return currentPolygons[id]!; |
|
||||
} |
|
||||
|
|
||||
final Set<String> _polygonIdsToRemove = prevPolygonIds.difference( |
|
||||
currentPolygonIds, |
|
||||
); |
|
||||
|
|
||||
final Set<Polygon> _polygonsToAdd = |
|
||||
currentPolygonIds |
|
||||
.difference(prevPolygonIds) |
|
||||
.map(idToCurrentPolygon) |
|
||||
.toSet(); |
|
||||
|
|
||||
bool hasChanged(Polygon current) { |
|
||||
final Polygon previous = previousPolygons[current.id]!; |
|
||||
return current != previous; |
|
||||
} |
|
||||
|
|
||||
final Set<Polygon> _polygonsToChange = |
|
||||
currentPolygonIds |
|
||||
.intersection(prevPolygonIds) |
|
||||
.map(idToCurrentPolygon) |
|
||||
.where(hasChanged) |
|
||||
.toSet(); |
|
||||
|
|
||||
polygonsToAdd = _polygonsToAdd; |
|
||||
polygonIdsToRemove = _polygonIdsToRemove; |
|
||||
polygonsToChange = _polygonsToChange; |
|
||||
} |
|
||||
|
|
||||
/// 想要添加的polygon对象集合. |
|
||||
Set<Polygon>? polygonsToAdd; |
|
||||
|
|
||||
/// 想要删除的polygon的id集合 |
|
||||
Set<String>? polygonIdsToRemove; |
|
||||
|
|
||||
/// 想要更新的polygon对象集合 |
|
||||
Set<Polygon>? polygonsToChange; |
|
||||
|
|
||||
/// 转换成可以序列化的map |
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> updateMap = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfNonNull(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
updateMap[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfNonNull('polygonsToAdd', serializeOverlaySet(polygonsToAdd!)); |
|
||||
addIfNonNull('polygonsToChange', serializeOverlaySet(polygonsToChange!)); |
|
||||
addIfNonNull('polygonIdsToRemove', polygonIdsToRemove?.toList()); |
|
||||
|
|
||||
return updateMap; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is! PolygonUpdates) return false; |
|
||||
final PolygonUpdates typedOther = other; |
|
||||
return setEquals(polygonsToAdd, typedOther.polygonsToAdd) && |
|
||||
setEquals(polygonIdsToRemove, typedOther.polygonIdsToRemove) && |
|
||||
setEquals(polygonsToChange, typedOther.polygonsToChange); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => |
|
||||
Object.hash(polygonsToAdd, polygonIdsToRemove, polygonsToChange); |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return '_PolygonUpdates{polygonsToAdd: $polygonsToAdd, ' |
|
||||
'polygonIdsToRemove: $polygonIdsToRemove, ' |
|
||||
'polygonsToChange: $polygonsToChange}'; |
|
||||
} |
|
||||
} |
|
||||
@ -1,198 +0,0 @@ |
|||||
// Copyright 2019 The Chromium Authors. All rights reserved. |
|
||||
// Use of this source code is governed by a BSD-style license that can be |
|
||||
// found in the LICENSE file. |
|
||||
|
|
||||
import 'package:amap_flutter_map/src/types/bitmap.dart'; |
|
||||
import 'package:flutter/foundation.dart' show listEquals; |
|
||||
import 'package:flutter/material.dart' show Color; |
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'base_overlay.dart'; |
|
||||
|
|
||||
/// 虚线类型 |
|
||||
enum DashLineType { |
|
||||
/// 不画虚线 |
|
||||
none, |
|
||||
|
|
||||
/// 方块样式 |
|
||||
square, |
|
||||
|
|
||||
/// 圆点样式 |
|
||||
circle, |
|
||||
} |
|
||||
|
|
||||
/// 线头类型 |
|
||||
enum CapType { |
|
||||
/// 普通头 |
|
||||
butt, |
|
||||
|
|
||||
/// 扩展头 |
|
||||
square, |
|
||||
|
|
||||
/// 箭头 |
|
||||
arrow, |
|
||||
|
|
||||
/// 圆形头 |
|
||||
round, |
|
||||
} |
|
||||
|
|
||||
/// 连接点类型 |
|
||||
enum JoinType { |
|
||||
/// 斜面连接点 |
|
||||
bevel, |
|
||||
|
|
||||
/// 斜接连接点 |
|
||||
miter, |
|
||||
|
|
||||
/// 圆角连接点 |
|
||||
round, |
|
||||
} |
|
||||
|
|
||||
/// 线相关的覆盖物类,内部的属性,描述了覆盖物的纹理、颜色、线宽等特征 |
|
||||
class Polyline extends BaseOverlay { |
|
||||
/// 默认构造函数 |
|
||||
Polyline({ |
|
||||
required this.points, |
|
||||
double width = 10, |
|
||||
this.visible = true, |
|
||||
this.geodesic = false, |
|
||||
double alpha = 1.0, |
|
||||
this.dashLineType = DashLineType.none, |
|
||||
this.capType = CapType.butt, |
|
||||
this.joinType = JoinType.bevel, |
|
||||
this.customTexture, |
|
||||
this.onTap, |
|
||||
this.color = const Color(0xCCC4E0F0), |
|
||||
}) : assert(points.length > 0), |
|
||||
this.width = (width <= 0 ? 10 : width), |
|
||||
this.alpha =(alpha < 0 ? 0 : (alpha > 1 ? 1 : alpha)), |
|
||||
super(); |
|
||||
|
|
||||
/// 覆盖物的坐标点数组,points不能为空 |
|
||||
final List<LatLng> points; |
|
||||
|
|
||||
/// 线宽,单位为逻辑像素,同Android中的dp,iOS中的point |
|
||||
final double width; |
|
||||
|
|
||||
/// 是否可见 |
|
||||
final bool visible; |
|
||||
|
|
||||
/// 透明度 |
|
||||
final double alpha; |
|
||||
|
|
||||
/// 覆盖物颜色,默认值为(0xCCC4E0F0). |
|
||||
final Color color; |
|
||||
|
|
||||
/// 自定义纹理图片,注意: 如果设置了自定义纹理图片,则color的设置将无效; |
|
||||
final BitmapDescriptor? customTexture; |
|
||||
|
|
||||
/// 是否为大地曲线 |
|
||||
final bool geodesic; |
|
||||
|
|
||||
/// 虚线类型 |
|
||||
final DashLineType dashLineType; |
|
||||
|
|
||||
/// 连接点类型 |
|
||||
final JoinType joinType; |
|
||||
|
|
||||
/// 线头类型 |
|
||||
final CapType capType; |
|
||||
|
|
||||
/// 点击回调(回调参数为id) |
|
||||
final ArgumentCallback<String>? onTap; |
|
||||
|
|
||||
/// 实际copy函数 |
|
||||
Polyline copyWith({ |
|
||||
List<LatLng>? pointsParam, |
|
||||
double? widthParam, |
|
||||
int? zIndexParam, |
|
||||
bool? visibleParam, |
|
||||
double? alphaParam, |
|
||||
DashLineType? dashLineTypeParam, |
|
||||
CapType? capTypeParam, |
|
||||
JoinType? joinTypeParam, |
|
||||
BitmapDescriptor? customTextureParam, |
|
||||
ArgumentCallback<String>? onTapParam, |
|
||||
Color? colorParam, |
|
||||
}) { |
|
||||
Polyline copyPolyline = Polyline( |
|
||||
points: pointsParam ?? points, |
|
||||
width: widthParam ?? width, |
|
||||
visible: visibleParam ?? visible, |
|
||||
geodesic: geodesic, |
|
||||
alpha: alphaParam ?? alpha, |
|
||||
dashLineType: dashLineTypeParam ?? dashLineType, |
|
||||
capType: capTypeParam ?? capType, |
|
||||
joinType: joinTypeParam ?? joinType, |
|
||||
customTexture: customTextureParam ?? customTexture, |
|
||||
onTap: onTapParam ?? onTap, |
|
||||
color: colorParam ?? color, |
|
||||
); |
|
||||
copyPolyline.setIdForCopy(id); |
|
||||
return copyPolyline; |
|
||||
} |
|
||||
|
|
||||
Polyline clone() => copyWith(); |
|
||||
|
|
||||
/// 将对象转换为可序列化的map. |
|
||||
@override |
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> json = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfPresent(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
json[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfPresent('id', id); |
|
||||
json['points'] = _pointsToJson(); |
|
||||
addIfPresent('width', width); |
|
||||
addIfPresent('visible', visible); |
|
||||
addIfPresent('geodesic', geodesic); |
|
||||
addIfPresent('alpha', alpha); |
|
||||
addIfPresent('dashLineType', dashLineType.index); |
|
||||
addIfPresent('capType', capType.index); |
|
||||
addIfPresent('joinType', joinType.index); |
|
||||
addIfPresent('customTexture', customTexture?.toMap()); |
|
||||
addIfPresent('color', color.value); |
|
||||
return json; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is !Polyline) return false; |
|
||||
final Polyline typedOther = other; |
|
||||
return id == typedOther.id && |
|
||||
listEquals(points, typedOther.points) && |
|
||||
width == typedOther.width && |
|
||||
visible == typedOther.visible && |
|
||||
geodesic == typedOther.geodesic && |
|
||||
alpha == typedOther.alpha && |
|
||||
dashLineType == typedOther.dashLineType && |
|
||||
capType == typedOther.capType && |
|
||||
joinType == typedOther.joinType && |
|
||||
color == typedOther.color; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => super.hashCode; |
|
||||
|
|
||||
dynamic _pointsToJson() { |
|
||||
final List<dynamic> result = <dynamic>[]; |
|
||||
for (final LatLng point in points) { |
|
||||
result.add(point.toJson()); |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Map<String, Polyline> keyByPolylineId(Iterable<Polyline> polylines) { |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (polylines == null) { |
|
||||
return <String, Polyline>{}; |
|
||||
} |
|
||||
return Map<String, Polyline>.fromEntries(polylines.map((Polyline polyline) => |
|
||||
MapEntry<String, Polyline>(polyline.id, polyline.clone()))); |
|
||||
} |
|
||||
@ -1,108 +0,0 @@ |
|||||
// Copyright 2018 The Chromium Authors. All rights reserved. |
|
||||
// Use of this source code is governed by a BSD-style license that can be |
|
||||
// found in the LICENSE file. |
|
||||
|
|
||||
import 'package:flutter/foundation.dart' show setEquals; |
|
||||
|
|
||||
import 'polyline.dart'; |
|
||||
import 'types.dart'; |
|
||||
|
|
||||
/// 该类主要用以描述[Polyline]的增删改等更新操作 |
|
||||
class PolylineUpdates { |
|
||||
/// 通过polyline的前后更新集合构造一个polylineUpdates |
|
||||
PolylineUpdates.from(Set<Polyline> previous, Set<Polyline> current) { |
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (previous == null) { |
|
||||
previous = Set<Polyline>.identity(); |
|
||||
} |
|
||||
|
|
||||
// ignore: unnecessary_null_comparison |
|
||||
if (current == null) { |
|
||||
current = Set<Polyline>.identity(); |
|
||||
} |
|
||||
|
|
||||
final Map<String, Polyline> previousPolylines = keyByPolylineId(previous); |
|
||||
final Map<String, Polyline> currentPolylines = keyByPolylineId(current); |
|
||||
|
|
||||
final Set<String> prevPolylineIds = previousPolylines.keys.toSet(); |
|
||||
final Set<String> currentPolylineIds = currentPolylines.keys.toSet(); |
|
||||
|
|
||||
Polyline idToCurrentPolyline(String id) { |
|
||||
return currentPolylines[id]!; |
|
||||
} |
|
||||
|
|
||||
final Set<String> _polylineIdsToRemove = prevPolylineIds.difference( |
|
||||
currentPolylineIds, |
|
||||
); |
|
||||
|
|
||||
final Set<Polyline> _polylinesToAdd = |
|
||||
currentPolylineIds |
|
||||
.difference(prevPolylineIds) |
|
||||
.map(idToCurrentPolyline) |
|
||||
.toSet(); |
|
||||
|
|
||||
bool hasChanged(Polyline current) { |
|
||||
final Polyline previous = previousPolylines[current.id]!; |
|
||||
return current != previous; |
|
||||
} |
|
||||
|
|
||||
final Set<Polyline> _polylinesToChange = |
|
||||
currentPolylineIds |
|
||||
.intersection(prevPolylineIds) |
|
||||
.map(idToCurrentPolyline) |
|
||||
.where(hasChanged) |
|
||||
.toSet(); |
|
||||
|
|
||||
polylinesToAdd = _polylinesToAdd; |
|
||||
polylineIdsToRemove = _polylineIdsToRemove; |
|
||||
polylinesToChange = _polylinesToChange; |
|
||||
} |
|
||||
|
|
||||
/// 用于添加polyline的集合 |
|
||||
Set<Polyline>? polylinesToAdd; |
|
||||
|
|
||||
/// 需要删除的plyline的id集合 |
|
||||
Set<String>? polylineIdsToRemove; |
|
||||
|
|
||||
/// 用于更新polyline的集合 |
|
||||
Set<Polyline>? polylinesToChange; |
|
||||
|
|
||||
/// 将对象装换为可序列化的对象 |
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> updateMap = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfNonNull(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
updateMap[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfNonNull('polylinesToAdd', serializeOverlaySet(polylinesToAdd!)); |
|
||||
addIfNonNull('polylinesToChange', serializeOverlaySet(polylinesToChange!)); |
|
||||
addIfNonNull('polylineIdsToRemove', polylineIdsToRemove?.toList()); |
|
||||
|
|
||||
return updateMap; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (other.runtimeType != runtimeType) return false; |
|
||||
if (other is! PolylineUpdates) return false; |
|
||||
final PolylineUpdates typedOther = other; |
|
||||
return setEquals(polylinesToAdd, typedOther.polylinesToAdd) && |
|
||||
setEquals(polylineIdsToRemove, typedOther.polylineIdsToRemove) && |
|
||||
setEquals(polylinesToChange, typedOther.polylinesToChange); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => |
|
||||
Object.hash(polylinesToAdd, polylineIdsToRemove, polylinesToChange); |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return '_PolylineUpdates{polylinesToAdd: $polylinesToAdd, ' |
|
||||
'polylineIdsToRemove: $polylineIdsToRemove, ' |
|
||||
'polylinesToChange: $polylinesToChange}'; |
|
||||
} |
|
||||
} |
|
||||
@ -1,11 +0,0 @@ |
|||||
export 'camera.dart'; |
|
||||
export 'ui.dart'; |
|
||||
export 'base_overlay.dart'; |
|
||||
export 'marker.dart'; |
|
||||
export 'marker_updates.dart'; |
|
||||
export 'polyline.dart'; |
|
||||
export 'polyline_updates.dart'; |
|
||||
export 'polygon.dart'; |
|
||||
export 'polygon_updates.dart'; |
|
||||
export 'camera.dart'; |
|
||||
export 'bitmap.dart'; |
|
||||
@ -1,257 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'dart:ui' show Color; |
|
||||
import 'package:amap_flutter_base/amap_flutter_base.dart'; |
|
||||
import 'package:amap_flutter_map/amap_flutter_map.dart'; |
|
||||
|
|
||||
/// 地图类型 |
|
||||
enum MapType { |
|
||||
/// 普通地图 |
|
||||
normal, |
|
||||
|
|
||||
/// 卫星地图 |
|
||||
satellite, |
|
||||
|
|
||||
/// 夜间视图 |
|
||||
night, |
|
||||
|
|
||||
/// 导航视图 |
|
||||
navi, |
|
||||
|
|
||||
/// 公交视图 |
|
||||
bus, |
|
||||
} |
|
||||
|
|
||||
// 设置摄像机的边界. |
|
||||
class CameraTargetBounds { |
|
||||
/// 使用指定的边界框或空值创建摄影机目标边界 |
|
||||
/// |
|
||||
/// 设置为null时代表不指定边界 |
|
||||
const CameraTargetBounds(this.bounds); |
|
||||
|
|
||||
/// 摄像机的边界. |
|
||||
/// |
|
||||
/// null代表不指定边界 |
|
||||
final LatLngBounds? bounds; |
|
||||
|
|
||||
/// 取消指定边界 |
|
||||
static const CameraTargetBounds unbounded = CameraTargetBounds(null); |
|
||||
|
|
||||
/// 转换成json对象 |
|
||||
dynamic toJson() => <dynamic>[bounds?.toJson()]; |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(dynamic other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (runtimeType != other.runtimeType) return false; |
|
||||
final CameraTargetBounds typedOther = other; |
|
||||
return bounds == typedOther.bounds; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => bounds.hashCode; |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return 'CameraTargetBounds(bounds: $bounds)'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// 地图最大最小缩放级别的封装对象 |
|
||||
class MinMaxZoomPreference { |
|
||||
/// 为地图创建一个不可变的最大最小缩放范围 |
|
||||
/// |
|
||||
/// 缩放级别范围为[3, 20],超出范围取边界值 |
|
||||
/// |
|
||||
const MinMaxZoomPreference(double minZoom, double maxZoom) |
|
||||
: this.minZoom = |
|
||||
((minZoom < 3 ? 3 : minZoom) > (maxZoom > 20 ? 20 : maxZoom) |
|
||||
? maxZoom |
|
||||
: minZoom), |
|
||||
this.maxZoom = |
|
||||
((minZoom < 3 ? 3 : minZoom) > (maxZoom > 20 ? 20 : maxZoom) |
|
||||
? minZoom |
|
||||
: maxZoom); |
|
||||
|
|
||||
/// 最小zoomLevel |
|
||||
final double? minZoom; |
|
||||
|
|
||||
/// 最大zoomLevel |
|
||||
final double? maxZoom; |
|
||||
|
|
||||
/// 高德地图默认zoomLevel的范围. |
|
||||
static const MinMaxZoomPreference defaultPreference = MinMaxZoomPreference( |
|
||||
3, |
|
||||
20, |
|
||||
); |
|
||||
|
|
||||
/// JSON序列化. |
|
||||
dynamic toJson() => <dynamic>[minZoom, maxZoom]; |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(dynamic other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (runtimeType != other.runtimeType) return false; |
|
||||
final MinMaxZoomPreference typedOther = other; |
|
||||
return minZoom == typedOther.minZoom && maxZoom == typedOther.maxZoom; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => Object.hash(minZoom, maxZoom); |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return 'MinMaxZoomPreference(minZoom: $minZoom, maxZoom: $maxZoom)'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
///定位小蓝点配置项 |
|
||||
class MyLocationStyleOptions { |
|
||||
///是否显示定位小蓝点 |
|
||||
bool enabled; |
|
||||
|
|
||||
///精度圈填充色 |
|
||||
Color? circleFillColor; |
|
||||
|
|
||||
///精度圈边框色 |
|
||||
Color? circleStrokeColor; |
|
||||
|
|
||||
///精度圈边框宽度 |
|
||||
double? circleStrokeWidth; |
|
||||
|
|
||||
///小蓝点图标 |
|
||||
BitmapDescriptor? icon; |
|
||||
|
|
||||
MyLocationStyleOptions( |
|
||||
this.enabled, { |
|
||||
this.circleFillColor, |
|
||||
this.circleStrokeColor, |
|
||||
this.circleStrokeWidth, |
|
||||
this.icon, |
|
||||
}); |
|
||||
|
|
||||
MyLocationStyleOptions clone() { |
|
||||
return MyLocationStyleOptions( |
|
||||
enabled, |
|
||||
circleFillColor: circleFillColor, |
|
||||
circleStrokeColor: circleStrokeColor, |
|
||||
circleStrokeWidth: circleStrokeWidth, |
|
||||
icon: icon, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
static MyLocationStyleOptions? fromMap(dynamic json) { |
|
||||
if (null == json) { |
|
||||
return null; |
|
||||
} |
|
||||
return MyLocationStyleOptions( |
|
||||
json['enabled'] ?? false, |
|
||||
circleFillColor: json['circleFillColor'] ?? null, |
|
||||
circleStrokeColor: json['circleStrokeColor'] ?? null, |
|
||||
circleStrokeWidth: json['circleStrokeWidth'] ?? null, |
|
||||
icon: json['icon'] ?? null, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Map<String, dynamic> toMap() { |
|
||||
final Map<String, dynamic> json = <String, dynamic>{}; |
|
||||
|
|
||||
void addIfPresent(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
json[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfPresent('enabled', enabled); |
|
||||
addIfPresent('circleFillColor', circleFillColor?.value); |
|
||||
addIfPresent('circleStrokeColor', circleStrokeColor?.value); |
|
||||
addIfPresent('circleStrokeWidth', circleStrokeWidth); |
|
||||
addIfPresent('icon', icon?.toMap()); |
|
||||
return json; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (runtimeType != other.runtimeType) return false; |
|
||||
if (other is! MyLocationStyleOptions) return false; |
|
||||
final MyLocationStyleOptions typedOther = other; |
|
||||
return enabled == typedOther.enabled && |
|
||||
circleFillColor == typedOther.circleFillColor && |
|
||||
circleStrokeColor == typedOther.circleStrokeColor && |
|
||||
icon == typedOther.icon; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return 'MyLocationOptionsStyle{' |
|
||||
'enabled: $enabled,' |
|
||||
'circleFillColor: $circleFillColor,' |
|
||||
'circleStrokeColor: $circleStrokeColor,' |
|
||||
'icon: $icon, }'; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => |
|
||||
Object.hash(enabled, circleFillColor, circleStrokeColor, icon); |
|
||||
} |
|
||||
|
|
||||
///地图自定义样式 |
|
||||
class CustomStyleOptions { |
|
||||
///开关项,是否开启自定义地图 |
|
||||
bool enabled; |
|
||||
|
|
||||
///自定义样式的二进制数据,对应下载的自定义地图文件中的style.data中的二进制数据 |
|
||||
Uint8List? styleData; |
|
||||
|
|
||||
///自定义扩展样式的二进制数据,对应下载的自定义地图文件中的style_extra.data中的二进制数据 |
|
||||
Uint8List? styleExtraData; |
|
||||
|
|
||||
CustomStyleOptions(this.enabled, {this.styleData, this.styleExtraData}); |
|
||||
|
|
||||
static CustomStyleOptions? fromMap(dynamic json) { |
|
||||
if (json == null) { |
|
||||
return null; |
|
||||
} |
|
||||
return CustomStyleOptions( |
|
||||
json['enabled'] ?? false, |
|
||||
styleData: json['styleData'] ?? null, |
|
||||
styleExtraData: json['styleExtraData'] ?? null, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
dynamic toMap() { |
|
||||
final Map<String, dynamic> json = <String, dynamic>{}; |
|
||||
void addIfPresent(String fieldName, dynamic value) { |
|
||||
if (value != null) { |
|
||||
json[fieldName] = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
addIfPresent('enabled', enabled); |
|
||||
addIfPresent('styleData', styleData); |
|
||||
addIfPresent('styleExtraData', styleExtraData); |
|
||||
return json; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool operator ==(Object other) { |
|
||||
if (identical(this, other)) return true; |
|
||||
if (runtimeType != other.runtimeType) return false; |
|
||||
if (other is! CustomStyleOptions) return false; |
|
||||
final CustomStyleOptions typedOther = other; |
|
||||
return enabled == typedOther.enabled && |
|
||||
styleData == typedOther.styleData && |
|
||||
styleExtraData == typedOther.styleExtraData; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
int get hashCode => Object.hash(enabled, styleData, styleExtraData); |
|
||||
|
|
||||
CustomStyleOptions clone() { |
|
||||
return CustomStyleOptions( |
|
||||
enabled, |
|
||||
styleData: styleData, |
|
||||
styleExtraData: styleExtraData, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
Binary file not shown.
@ -1,2 +1,2 @@ |
|||||
#Sun May 18 21:39:38 IST 2025 |
#Wed May 21 01:03:35 CST 2025 |
||||
gradle.version=8.10 |
gradle.version=8.9 |
||||
|
|||||
Loading…
Reference in new issue