From 4b1a1326e8f400048f79149b1657862a98a02f2d Mon Sep 17 00:00:00 2001 From: Kuloud Date: Thu, 4 Jan 2024 18:48:31 +0800 Subject: [PATCH] release 1.0.4 --- CHANGELOG.md | 4 + README.md | 2 +- example/ios/Podfile.lock | 7 - example/lib/data/demos.dart | 9 + example/lib/main.dart | 32 +- .../pages/map/map_with_extension_page.dart | 4 +- .../pages/overlays/custom_info_window.dart | 294 ++++++++++++++++++ example/pubspec.yaml | 3 +- lib/src/amap_controller.dart | 90 +++--- lib/src/amap_initializer.dart | 15 +- lib/src/amap_loader.dart | 36 ++- lib/src/amap_widget.dart | 14 +- .../buildin/info_window_extension.dart | 121 +++++++ lib/src/extension/extensions.dart | 1 + lib/src/types/amap_extension.dart | 55 +++- lib/src/types/extension_context.dart | 13 + lib/src/types/types.dart | 2 + pubspec.yaml | 7 +- 18 files changed, 606 insertions(+), 103 deletions(-) create mode 100644 example/lib/pages/overlays/custom_info_window.dart create mode 100644 lib/src/extension/buildin/info_window_extension.dart create mode 100644 lib/src/extension/extensions.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9a432..a3f8e4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.4 +2024-01-04. +* 添加 InfoWindowExtension,支持自定义 InfoWindow 样式 + ## 1.0.3+1 2024-01-03. * 处理lint,更新文档 diff --git a/README.md b/README.md index d2a547c..adc86f3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ class DemoWidget extends State { @override Widget build(BuildContext context) { - AMapInitializer.init(context, ConstConfig.amapApiKeys); + AMapInitializer.init(context, apiKey: ConstConfig.amapApiKeys); return Scaffold( // ... diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e620171..1f3ab61 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -4,9 +4,6 @@ PODS: - amap_map (1.0.3): - AMap3DMap - Flutter - - amap_map_extensions (0.0.1): - - AMap3DMap - - Flutter - AMapFoundation (1.8.2) - Flutter (1.0.0) - permission_handler_apple (9.1.1): @@ -14,7 +11,6 @@ PODS: DEPENDENCIES: - amap_map (from `.symlinks/plugins/amap_map/ios`) - - amap_map_extensions (from `.symlinks/plugins/amap_map_extensions/ios`) - Flutter (from `Flutter`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) @@ -26,8 +22,6 @@ SPEC REPOS: EXTERNAL SOURCES: amap_map: :path: ".symlinks/plugins/amap_map/ios" - amap_map_extensions: - :path: ".symlinks/plugins/amap_map_extensions/ios" Flutter: :path: Flutter permission_handler_apple: @@ -36,7 +30,6 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: AMap3DMap: dce25dd3e51e6b92109caa7d0c97fc6055830fb3 amap_map: 8773e5cacc760edf208b1e6e61000241d26385fa - amap_map_extensions: 6d2affabf1ef14c7af8aa9eeb815b8b8453d92b0 AMapFoundation: 9885c48fc3a78fdfb84a0299a2293e56ea3c9fec Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 diff --git a/example/lib/data/demos.dart b/example/lib/data/demos.dart index 364dc14..af7d01b 100644 --- a/example/lib/data/demos.dart +++ b/example/lib/data/demos.dart @@ -6,6 +6,7 @@ import 'package:amap_map_example/pages/map/limit_map_bounds.dart'; import 'package:amap_map_example/pages/map/map_my_location.dart'; import 'package:amap_map_example/pages/map/map_with_extension_page.dart'; import 'package:amap_map_example/pages/map/show_map_page.dart'; +import 'package:amap_map_example/pages/overlays/custom_info_window.dart'; import 'package:amap_map_example/pages/overlays/marker_config.dart'; import 'package:flutter/material.dart'; @@ -71,6 +72,14 @@ List overlayDemos() { slug: 'marker-config', configurations: [ DemoConfiguration(buildRoute: (context) => MarkerConfigDemoPage()) + ]), + Demo( + title: '自定义InfoWindow', + category: DemoCategory.overlay, + subtitle: '自定义与Marker绑定的InfoWindow样式', + slug: 'custom-info-window', + configurations: [ + DemoConfiguration(buildRoute: (context) => CustomInfoWindowDemoPage()) ]) ]; } diff --git a/example/lib/main.dart b/example/lib/main.dart index 799b3ab..d9c5d67 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -8,36 +8,6 @@ import 'package:amap_map_example/theme.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; -// final List _mapDemoPages = [ -// AllMapConfigDemoPage('总体演示', '演示AMapWidget的所有配置项'), - -// MinMaxZoomDemoPage('指定显示级别范围', '演示指定最小最大级别功能'), -// ChangeMapTypePage('切换地图图层', '演示内置的地图图层'), -// CustomMapStylePage('自定义地图', '根据自定义的地图样式文件显示地图'), -// MultiMapDemoPage('地图多实例', '同时显示多个地图'), -// ]; - -// final List _interactiveDemoPages = [ -// MapUIDemoPage('UI控制', 'ui开关演示'), -// GesturesDemoPage('手势交互', '手势交互'), -// PoiClickDemoPage('点击poi功能', '演示点击poi之后的回调和信息透出'), -// MoveCameraDemoPage('改变地图视角', '演示改变地图的中心点、可视区域、缩放级别等功能'), -// SnapshotPage('地图截屏', '地图截屏示例'), -// ]; - -// final List _markerPages = [ -// MarkerAddWithMapPage("随地图添加", "演示初始化地图时直接添加marker"), -// MarkerAddAfterMapPage("单独添加", "演示地图初始化之后单独添加marker功能"), -// MarkerCustomIconPage('自定义图标', '演示marker使用自定义图标功能'), -// ]; - -// final List _overlayPages = [ -// PolylineDemoPage('Polyline操作', '演示Polyline的相关属性的操作'), -// PolylineGeodesicDemoPage('Polyline大地曲线', '演示大地曲线的添加'), -// PolylineTextureDemoPage('Polyline纹理线', '演示纹理线的添加'), -// PolygonDemoPage('Polygon操作', '演示Polygon的相关属性的操作'), -// ]; - final List needPermissionList = [ Permission.location, Permission.storage, @@ -82,7 +52,7 @@ class _AMapDemoState extends State @override Widget build(BuildContext context) { - AMapInitializer.init(context, ConstConfig.amapApiKeys); + AMapInitializer.init(context, apiKey: ConstConfig.amapApiKeys); AMapInitializer.updatePrivacyAgree(ConstConfig.amapPrivacyStatement); return Scaffold( appBar: AppBar(title: const Text('高德地图示例')), diff --git a/example/lib/pages/map/map_with_extension_page.dart b/example/lib/pages/map/map_with_extension_page.dart index a8c77c2..edf726e 100644 --- a/example/lib/pages/map/map_with_extension_page.dart +++ b/example/lib/pages/map/map_with_extension_page.dart @@ -10,7 +10,7 @@ // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, -import 'package:amap_map_extensions/amap_map_extensions.dart'; +// import 'package:amap_map_extensions/amap_map_extensions.dart'; import 'package:flutter/material.dart'; import 'dart:typed_data'; @@ -24,7 +24,7 @@ class MapWithExtensionPage extends StatefulWidget { class _MapWithExtensionPageState extends State { List _approvalNumberWidget = []; - final _extension = AmapMapExtensions(); + // final _extension = AmapMapExtensions(); @override Widget build(BuildContext context) { diff --git a/example/lib/pages/overlays/custom_info_window.dart b/example/lib/pages/overlays/custom_info_window.dart new file mode 100644 index 0000000..aa2b602 --- /dev/null +++ b/example/lib/pages/overlays/custom_info_window.dart @@ -0,0 +1,294 @@ +import 'dart:async'; + +import 'package:amap_map_example/widgets/amap_switch_button.dart'; +import 'package:flutter/material.dart'; + +import 'package:amap_map/amap_map.dart'; +import 'package:x_amap_base/x_amap_base.dart'; +import 'dart:math'; + +/// 自定义[InfoWindow]用例 +class CustomInfoWindowDemoPage extends StatefulWidget { + const CustomInfoWindowDemoPage(); + + @override + State createState() => _State(); +} + +class _State extends State { + static final LatLng mapCenter = const LatLng(39.909187, 116.397451); + + Map _markers = {}; + BitmapDescriptor? _markerIcon; + String? selectedMarkerId; + bool showInfoWindow = false; + + final _infoWindowExtension = InfoWindowExtension( + infoWindow: Container( + color: Colors.lightBlue.shade400, + child: Text('info'), + ), + option: InfoWindowOption(latLng: mapCenter)); + + Future _onMapCreated(AMapController controller) async {} + + void _add() { + final int markerCount = _markers.length; + LatLng markPostion = LatLng( + mapCenter.latitude + sin(markerCount * pi / 12.0) / 20.0, + mapCenter.longitude + cos(markerCount * pi / 12.0) / 20.0); + final Marker marker = Marker( + position: markPostion, + icon: _markerIcon!, + infoWindow: InfoWindow(title: '第 $markerCount 个Marker'), + onTap: (markerId) => _onMarkerTapped(markerId), + onDragEnd: (markerId, endPosition) => + _onMarkerDragEnd(markerId, endPosition), + ); + + setState(() { + _markers[marker.id] = marker; + }); + } + + void _onMarkerTapped(String markerId) { + final Marker? tappedMarker = _markers[markerId]; + final String? title = tappedMarker!.infoWindow.title; + print('$title 被点击了,markerId: $markerId'); + setState(() { + selectedMarkerId = markerId; + }); + } + + void _onMarkerDragEnd(String markerId, LatLng position) { + final Marker? tappedMarker = _markers[markerId]; + final String? title = tappedMarker!.infoWindow.title; + print('$title markerId: $markerId 被拖拽到了: $position'); + } + + void _remove() { + final Marker? selectedMarker = _markers[selectedMarkerId]; + //有选中的Marker + if (selectedMarker != null) { + setState(() { + _markers.remove(selectedMarkerId); + }); + } else { + print('无选中的Marker,无法删除'); + } + } + + void _removeAll() { + if (_markers.length > 0) { + setState(() { + _markers.clear(); + selectedMarkerId = null.toString(); + }); + } + } + + void _changeInfo() async { + final Marker marker = _markers[selectedMarkerId]!; + final String newTitle = marker.infoWindow.title! + '*'; + if (selectedMarkerId != null) { + setState(() { + _markers[selectedMarkerId!] = marker.copyWith( + infoWindowParam: marker.infoWindow.copyWith( + titleParam: newTitle, + ), + ); + }); + } + } + + void _changeAnchor() { + final Marker marker = _markers[selectedMarkerId]!; + final Offset currentAnchor = marker.anchor; + double dx = 0; + double dy = 0; + if (currentAnchor.dx < 1) { + dx = currentAnchor.dx + 0.1; + } else { + dx = 0; + } + if (currentAnchor.dy < 1) { + dy = currentAnchor.dy + 0.1; + } else { + dy = 0; + } + final Offset newAnchor = Offset(dx, dy); + setState(() { + _markers[selectedMarkerId!] = marker.copyWith( + anchorParam: newAnchor, + ); + }); + } + + void _changePosition() { + final Marker marker = _markers[selectedMarkerId]!; + final LatLng current = marker.position; + final Offset offset = Offset( + mapCenter.latitude - current.latitude, + mapCenter.longitude - current.longitude, + ); + setState(() { + _markers[selectedMarkerId!] = marker.copyWith( + positionParam: LatLng( + mapCenter.latitude + offset.dy, + mapCenter.longitude + offset.dx, + ), + ); + }); + } + + Future _changeAlpha() async { + final Marker marker = _markers[selectedMarkerId]!; + final double current = marker.alpha; + setState(() { + _markers[selectedMarkerId!] = marker.copyWith( + alphaParam: current < 0.1 ? 1.0 : current * 0.75, + ); + }); + } + + Future _changeRotation() async { + final Marker marker = _markers[selectedMarkerId]!; + final double current = marker.rotation; + setState(() { + _markers[selectedMarkerId!] = marker.copyWith( + rotationParam: current == 330.0 ? 0.0 : current + 30.0, + ); + }); + } + + Future _toggleVisible(value) async { + final Marker marker = _markers[selectedMarkerId]!; + + setState(() { + showInfoWindow = value; + _markers[selectedMarkerId!] = marker.copyWith( + // visibleParam: value, + ); + }); + } + + Future _toggleDraggable(value) async { + final Marker marker = _markers[selectedMarkerId]!; + setState(() { + _markers[selectedMarkerId!] = marker.copyWith( + draggableParam: value, + ); + }); + } + + @override + Widget build(BuildContext context) { + ///以下几种获取自定图片的方式使用其中一种即可。 + //最简单的方式 + if (null == _markerIcon) { + _markerIcon = BitmapDescriptor.fromIconPath('assets/location_marker.png'); + } + + _infoWindowExtension.option?.show = + _markers[selectedMarkerId] != null && showInfoWindow; + _infoWindowExtension.option?.latLng = _markers[selectedMarkerId]?.position; + + final AMapWidget map = AMapWidget( + onMapCreated: _onMapCreated, + markers: Set.of(_markers.values), + extensions: [_infoWindowExtension], + ); + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + height: MediaQuery.of(context).size.height * 0.6, + width: MediaQuery.of(context).size.width, + child: map, + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + child: const Text('添加'), + onPressed: _add, + ), + TextButton( + child: const Text('移除'), + onPressed: + (selectedMarkerId == null) ? null : _remove, + ), + TextButton( + child: const Text('更新InfoWidow'), + onPressed: + (selectedMarkerId == null) ? null : _changeInfo, + ), + TextButton( + child: const Text('修改锚点'), + onPressed: (selectedMarkerId == null) + ? null + : _changeAnchor, + ), + TextButton( + child: const Text('修改透明度'), + onPressed: (selectedMarkerId == null) + ? null + : _changeAlpha, + ), + ], + ), + Column( + children: [ + TextButton( + child: const Text('全部移除'), + onPressed: _markers.length > 0 ? _removeAll : null, + ), + AMapSwitchButton( + label: Text('允许拖动'), + onSwitchChanged: (selectedMarkerId == null) + ? null + : _toggleDraggable, + defaultValue: false, + ), + AMapSwitchButton( + label: Text('显示'), + onSwitchChanged: (selectedMarkerId == null) + ? null + : _toggleVisible, + defaultValue: true, + ), + TextButton( + child: const Text('修改坐标'), + onPressed: (selectedMarkerId == null) + ? null + : _changePosition, + ), + TextButton( + child: const Text('修改旋转角度'), + onPressed: (selectedMarkerId == null) + ? null + : _changeRotation, + ), + ], + ), + ], + ) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 003577e..6bf7e83 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -23,7 +23,8 @@ dependencies: # the parent directory to use the current plugin's version. path: ../ - amap_map_extensions: ^0.0.1 + # amap_map_extensions: ^0.0.1 + # amap_map_extensions: # path: ../../amap_map_extensions diff --git a/lib/src/amap_controller.dart b/lib/src/amap_controller.dart index 0a45f0a..2960c08 100644 --- a/lib/src/amap_controller.dart +++ b/lib/src/amap_controller.dart @@ -1,12 +1,12 @@ // Copyright 2023-2024 kuloud // Copyright 2020 lbs.amap.com - +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 - +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -48,48 +48,52 @@ class AMapController { } 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.onLocationChanged(mapId: mapId).listen( + (LocationChangedEvent e) => + _mapState.widget.onLocationChanged?.call(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)); + .onCameraMove(mapId: mapId) + .listen((CameraPositionMoveEvent e) { + _mapState._extensions.values.forEach((ext) => ext.onCameraMove(e.value)); + _mapState.widget.onCameraMove?.call(e.value); + }); _methodChannel - .onPolylineTap(mapId: mapId) - .listen((PolylineTapEvent e) => _mapState.onPolylineTap(e.value)); + .onCameraMoveEnd(mapId: mapId) + .listen((CameraPositionMoveEndEvent e) { + _mapState._extensions.values + .forEach((ext) => ext.onCameraMoveEnd(e.value)); + _mapState.widget.onCameraMoveEnd?.call(e.value); + }); + _methodChannel + .onMapTap(mapId: mapId) + .listen(((MapTapEvent e) => _mapState.widget.onTap?.call(e.value))); + _methodChannel.onMapLongPress(mapId: mapId).listen(((MapLongPressEvent e) { + _mapState._extensions.values.forEach((ext) => ext.onLongPress(e.value)); + _mapState.widget.onLongPress?.call(e.value); + })); + + _methodChannel.onPoiTouched(mapId: mapId).listen(((MapPoiTouchEvent e) { + _mapState._extensions.values.forEach((ext) => ext.onPoiTouched(e.value)); + _mapState.widget.onPoiTouched?.call(e.value); + })); + + _methodChannel.onMarkerTap(mapId: mapId).listen((MarkerTapEvent e) { + _mapState._extensions.values.forEach((ext) => ext.onMarkerTap(e.value)); + _mapState.onMarkerTap(e.value); + }); + + _methodChannel.onMarkerDragEnd(mapId: mapId).listen((MarkerDragEndEvent e) { + _mapState._extensions.values + .forEach((ext) => ext.onMarkerDragEnd(e.value)); + _mapState.onMarkerDragEnd(e.value, e.position); + }); + + _methodChannel.onPolylineTap(mapId: mapId).listen((PolylineTapEvent e) { + _mapState._extensions.values.forEach((ext) => ext.onPolylineTap(e.value)); + _mapState.onPolylineTap(e.value); + }); } void disponse() { @@ -137,12 +141,12 @@ class AMapController { return _methodChannel.clearDisk(mapId: mapId); } - /// 经纬度转屏幕坐标 + /// 经纬度转屏幕坐标 since v1.0.3 Future toScreenCoordinate(LatLng latLng) { return _methodChannel.toScreenLocation(latLng, mapId: mapId); } - /// 屏幕坐标转经纬度 + /// 屏幕坐标转经纬度 From v1.0.3 Future fromScreenCoordinate(ScreenCoordinate screenCoordinate) { return _methodChannel.fromScreenLocation(screenCoordinate, mapId: mapId); } diff --git a/lib/src/amap_initializer.dart b/lib/src/amap_initializer.dart index 70068f7..1e7e995 100644 --- a/lib/src/amap_initializer.dart +++ b/lib/src/amap_initializer.dart @@ -1,11 +1,11 @@ // Copyright 2023-2024 kuloud - +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 - +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,7 +20,12 @@ class AMapInitializer { AMapInitializer._(); - static Future init(BuildContext context, AMapApiKey apiKey) async { + /// 初始化地图组件 + /// + /// @param context 用于图片资源适配屏幕密度,需在[AMapWidget]使用前调用[init]方法 + /// @param apiKey 申请的 AMapSDK Key,如果熟悉原生侧集成配置,可以参考高德文档集成,可选 + /// + static void init(BuildContext context, {AMapApiKey? apiKey}) { AMapUtil.init(context); _apiKey = apiKey; } @@ -33,7 +38,7 @@ class AMapInitializer { /// /// 高德SDK合规使用方案请参考:https://lbs.amap.com/news/sdkhgsy /// - static updatePrivacyAgree(AMapPrivacyStatement privacyStatement) { + static void updatePrivacyAgree(AMapPrivacyStatement privacyStatement) { _privacyStatement = privacyStatement; } } diff --git a/lib/src/amap_loader.dart b/lib/src/amap_loader.dart index d41fb05..41b33d1 100644 --- a/lib/src/amap_loader.dart +++ b/lib/src/amap_loader.dart @@ -1,16 +1,17 @@ // Copyright 2023-2024 kuloud - +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 - +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, part of amap_map; +/// 加载器,在[AMapWidget]的生命周期上绑定拓展 class AMapLoader extends StatefulWidget { const AMapLoader( {super.key, required this.mapView, required this.extensions}); @@ -22,19 +23,32 @@ class AMapLoader extends StatefulWidget { State createState() => _AMapLoaderState(); static void prepare() { - // TODO loop handle with extensions + // TODO Loader 初始化静态方法,在initState时做些逻辑 } + /// 传递构建视图,拓展按需进行包装和挂载定制视图 Widget buildFromExtension(AMapContext aMapContext) { - // TODO loop handle with extensions - return mapView; + Widget child = mapView; + for (var e in extensions) { + child = e.build(aMapContext, child); + } + return child; } + /// [didChangeDependencies] 状态更新时,挨个遍历进行拓展的更新操作 void prepareFromExtension(AMapContext aMapContext) { for (var e in extensions) { e.prepare(aMapContext); } } + + /// [didChangeDependencies] 状态更新时,挨个遍历进行拓展的更新操作 + void updateFromExtension(AMapContext aMapContext) { + for (var e in extensions) { + e.update(aMapContext); + } + // Future.forEach(extensions, (ext) => ext.update(aMapContext)); + } } class _AMapLoaderState extends State { @@ -48,6 +62,16 @@ class _AMapLoaderState extends State { super.didChangeDependencies(); } + @override + void didUpdateWidget(covariant AMapLoader oldWidget) { + final aMapContext = AMapContext( + buildContext: context, + currentStep: CurrentStep.updating, + loader: widget); + widget.updateFromExtension(aMapContext); + super.didUpdateWidget(oldWidget); + } + @override void dispose() { for (var e in widget.extensions) { diff --git a/lib/src/amap_widget.dart b/lib/src/amap_widget.dart index bc95b9e..e9625f9 100644 --- a/lib/src/amap_widget.dart +++ b/lib/src/amap_widget.dart @@ -220,6 +220,7 @@ class _MapState extends State { _updateMarkers(); _updatePolylines(); _updatePolygons(); + _updateExtensions(); } Future onPlatformViewCreated(int id) async { @@ -231,8 +232,12 @@ class _MapState extends State { _controller.complete(controller); if (_extensions.isNotEmpty) { - await Future.forEach( - _extensions.values, (e) => e.bindMethodChannel(controller.channel)); + debugPrint('[onPlatformViewCreated] $controller'); + await Future.forEach(_extensions.values, (e) { + debugPrint('[onPlatformViewCreated] controller: ${controller.mapId}'); + e.bindMethodChannel(controller.channel); + e.bindMapController(controller); + }); } final MapCreatedCallback? _onMapCreated = widget.onMapCreated; @@ -304,6 +309,11 @@ class _MapState extends State { PolygonUpdates.from(_polygons.values.toSet(), widget.polygons)); _polygons = keyByPolygonId(widget.polygons); } + + void _updateExtensions() async { + // final AMapController controller = await _controller.future; + _extensions = keyByExtensionId(widget.extensions); + } } //高德地图参数设置 diff --git a/lib/src/extension/buildin/info_window_extension.dart b/lib/src/extension/buildin/info_window_extension.dart new file mode 100644 index 0000000..8f17300 --- /dev/null +++ b/lib/src/extension/buildin/info_window_extension.dart @@ -0,0 +1,121 @@ +import 'dart:async'; + +import 'package:amap_map/amap_map.dart'; +import 'package:flutter/material.dart'; +import 'package:x_amap_base/x_amap_base.dart'; + +/// [amap_map]插件支持默认的[InfoWindow]展示[title]和[snippet]字段,如果要自定义 +/// [InfoWindow],需要集成此内置插件。 +/// +/// ```dart +/// AMapWidget map = AMapWidget( +/// onMapCreated: _onMapCreated, +/// markers: Set.of(_markers.values), +/// extensions: [ +/// InfoWindowExtension( +/// infoWindow: Container( +/// child: Text('Custom widget'), +/// )) +/// ], +/// ) +/// ``` +/// +/// 此插件会在[AMapWidget]外侧套一层[Stack],根据屏幕坐标和经纬度进行适配自定义视图 +/// +/// ```dart +/// Stack( +/// children: [ +/// child, +/// Positioned( +/// top: 0, +/// left: 0, +/// child: Visibility(child: infoWindow), +/// ), +/// ], +/// ) +/// ``` +class InfoWindowExtension extends AMapExtension { + InfoWindowExtension({required this.infoWindow, this.option}); + + StreamController _streamController = + StreamController.broadcast(); + + final Widget infoWindow; + InfoWindowOption? option; + + AMapController? mapController; + double? _x; + double? _y; + + @override + void update(AMapContext aMapContext) { + _update(); + } + + @override + onCameraMove(CameraPosition value) { + _update(); + } + + _update() async { + if (mapController == null || option?.latLng == null) { + return; + } + + final coordinate = await mapController!.toScreenCoordinate(option!.latLng!); + + _x = coordinate.x.toDouble(); + _y = coordinate.y.toDouble(); + + _streamController.add(this); + } + + @override + bindMapController(AMapController controller) { + debugPrint('[InfoWindowExtension] bindMapController: $controller'); + mapController = controller; + } + + @override + Widget build(AMapContext aMapContext, Widget child) { + return Stack( + children: [ + child, + StreamBuilder( + key: ValueKey(this), + initialData: this, + stream: _streamController.stream, + builder: (context, snapshot) { + final data = snapshot.data; + if (data == null || + !(data.option?.show ?? false) || + data._x == null) { + return Container(); + } + + return Positioned( + top: data._y, + left: data._x, + child: Container( + margin: data.option?.offset, + child: infoWindow, + ), + ); + }), + ], + ); + } +} + +class InfoWindowOption { + bool show = false; + LatLng? latLng; + EdgeInsetsGeometry? offset; + + InfoWindowOption({this.show = false, this.latLng, this.offset}); + + @override + String toString() { + return 'InfoWindowOption{show: $show, latLng: $latLng, offset: $offset}'; + } +} diff --git a/lib/src/extension/extensions.dart b/lib/src/extension/extensions.dart new file mode 100644 index 0000000..c41447d --- /dev/null +++ b/lib/src/extension/extensions.dart @@ -0,0 +1 @@ +export 'buildin/info_window_extension.dart'; diff --git a/lib/src/types/amap_extension.dart b/lib/src/types/amap_extension.dart index b52f40b..b37248b 100644 --- a/lib/src/types/amap_extension.dart +++ b/lib/src/types/amap_extension.dart @@ -1,7 +1,21 @@ +// Copyright 2023-2024 kuloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, + +import 'package:amap_map/amap_map.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:x_amap_base/x_amap_base.dart'; -import 'types.dart'; - +/// 插件拓展,绑定[AMapWidget]的生命周期和控制器,实现基于地图的拓展能力(通常是地图交互的可 +/// 选功能) abstract class AMapExtension { late String _id; @@ -11,11 +25,48 @@ abstract class AMapExtension { this._id = this.hashCode.toString(); } + /// 视图层级变化 + /// since v1.0.4 void prepare(AMapContext aMapContext) {} + /// 视图变化 + /// since v1.0.4 + void update(AMapContext aMapContext) {} + void onDispose() {} + @Deprecated('使用 bindMapController ,计划在1.0.6移除') bindMethodChannel(MethodChannel channel) {} + + /// 注入[AMapController] 方便地图交互 + bindMapController(AMapController controller) {} + + Widget build(AMapContext aMapContext, Widget child) { + /// 默认传递[AMapWidget],有部分场景需要在[AMapWidget]外包装容器,根据传递拓展列表顺序 + /// 链式执行 + return child; + } + + /// since v1.0.4 + onCameraMove(CameraPosition cameraPosition) {} + + /// since v1.0.4 + onCameraMoveEnd(CameraPosition cameraPosition) {} + + /// since v1.0.4 + onLongPress(LatLng latLng) {} + + /// since v1.0.4 + onPoiTouched(AMapPoi poi) {} + + /// since v1.0.4 + onMarkerTap(String value) {} + + /// since v1.0.4 + onMarkerDragEnd(String value) {} + + /// since v1.0.4 + onPolylineTap(String value) {} } Map keyByExtensionId( diff --git a/lib/src/types/extension_context.dart b/lib/src/types/extension_context.dart index a76f8d2..db1e356 100644 --- a/lib/src/types/extension_context.dart +++ b/lib/src/types/extension_context.dart @@ -1,6 +1,18 @@ +// Copyright 2023-2024 kuloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, + import 'package:amap_map/amap_map.dart'; import 'package:flutter/material.dart'; +/// 上下文 class AMapContext { final BuildContext buildContext; final CurrentStep currentStep; @@ -14,5 +26,6 @@ class AMapContext { enum CurrentStep { preparing, + updating, building, } diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index f12ee06..85d0594 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -23,3 +23,5 @@ export 'bitmap.dart'; export 'extension_context.dart'; export 'amap_extension.dart'; export 'screen_coordinate.dart'; + +export '../extension/extensions.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 63ab099..2b704fa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: amap_map description: Amap SDK Flutter plugin for integrating AMapSDK in iOS and Android applications. -version: 1.0.3+1 +version: 1.0.4 homepage: https://github.com/kuloud/amap_map issue_tracker: https://github.com/kuloud/amap_map/issues platforms: @@ -18,10 +18,11 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.17 - plugin_platform_interface: ^2.0.0 + plugin_platform_interface: ^2.1.8 stream_transform: ^2.0.0 - x_amap_base: ^1.0.0+2 + x_amap_base: ^1.0.1 + # provider: ^6.1.1 # x_amap_base: # path: ../x_amap_base dev_dependencies: