Mapbox Flutter:要素点击弹出弹窗实现方法求助
Flutter mapbox_gl ~0.16.0 点击要素弹窗实现问题
我正在用mapbox_gl: ~0.16.0开发Flutter应用,现在没法实现用户点击地图要素时显示弹窗的功能。没找到插件自带的合适弹窗控件或类似功能,不知道该怎么加这个特性。
简化代码
视图代码
MapboxMap( onMapCreated: (mController) { mapboxMapController = mController; controller.onMapCreated(mController); }, zoomGesturesEnabled: true, accessToken: "<MAPBOX ACCESS TOKEN>", initialCameraPosition: const CameraPosition( target: LatLng(47.59051426075817, -107.77329953835964), zoom: 15.0), trackCameraPosition: true, onCameraIdle: () { controller.getView(mapboxMapController); }, styleString: "mapbox://styles/mapbox/outdoors-v12?optimize=true&attribution=false&logo=false", rotateGesturesEnabled: false, tiltGesturesEnabled: false, );
控制器代码
void onMapCreated(MapboxMapController mapboxMap) async { var bounds = await mapboxMap.getVisibleRegion(); double northeastLat = bounds.northeast.latitude; double northeastLng = bounds.northeast.longitude; double northwestLat = bounds.northeast.latitude; double northwestLng = bounds.southwest.longitude; double southeastLat = bounds.southwest.latitude; double southeastLng = bounds.northeast.longitude; double southwestLat = bounds.southwest.latitude; double southwestLng = bounds.southwest.longitude; String queryString = "myAPI.php?northeastLat=$northeastLat&northeastLng=$northeastLng&" "northwestLat=$northwestLat&northwestLng=$northwestLng&" "southeastLat=$southeastLat&southeastLng=$southeastLng&" "southwestLat=$southwestLat&southwestLng=$southwestLng&" "zmLvl=15&uid=100"; Map<String, dynamic> mappedData = {}; var servResJson = await sendDataServer(queryString, mappedData); var servRes = jsonDecode(jsonEncode(servResJson.data)); geojson = Map<String, Object>.from(servRes); await Future.wait([ mapboxMap.removeLayer("fills-count"), mapboxMap.removeLayer("fills-circle"), mapboxMap.removeLayer("fills-fills"), mapboxMap.removeSource("fills"), mapboxMap.addSource( "fills", GeojsonSourceProperties( data: geojson, ), ), mapboxMap.addFillLayer( "fills", "fills-fills", FillLayerProperties( fillColor: [ "match", ["get", "status"], 1, statusColors[1], // Status 1 - Blue 2, statusColors[2], // Status 2 - Yellow 3, statusColors[3], // Status 3 - Green 4, statusColors[4], // Status 4 - Transparent convertColorToRgba( const Color.fromARGB(128, 246, 246, 246), 0), // Default color ], fillAntialias: true, fillOpacity: 0.6, fillOutlineColor: 'rgba(0, 0, 0, 1)', ), filter: ["has", "status"], enableInteraction: true, ), ]); update(); }
API返回的GeoJSON示例
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "id": "56019", "status": 2, "geometry": { "type": "Polygon", "coordinates": [ [ [ -107.774000467, 47.598100673 ], [ -107.765000467, 47.598100673 ], [ -107.765000467, 47.589100673 ], [ -107.774000467, 47.589100673 ], [ -107.774000467, 47.598100673 ] ] ] }, "properties": { "dbId": "56019", "status": 2, "uid": 100, "isBlocked": 0, "id": "56019", "left": -107.774000467, "top": 47.598100673, "right": -107.765000467, "bottom": 47.589100673 } } // 更多要素省略 ] }
已尝试的代码
我能通过下面的代码获取到要素ID,但不知道不用自定义对话框的话,能不能用插件自带的弹窗实现显示ID的效果(效果是点击要素后弹出包含ID的气泡框):
mapboxMap.onFeatureTapped.add((id, point, coordinates) { if (id != null) { debugPrint( 'Tapped feature with ID - $id, Point - $point, Coordinates - $coordinates'); } });
解决方案
首先明确:mapbox_gl: ~0.16.0本身没有自带的气泡弹窗组件,你需要自己实现悬浮弹窗,或者用Flutter原生的对话框结合坐标转换来完成,下面是两种可行方案:
方案1:自定义地图气泡弹窗
- 用
Stack包裹MapboxMap和弹窗,让两者处于同一层级。 - 维护状态变量存储弹窗的显示状态、位置和要素ID。
- 在
onFeatureTapped回调中,把经纬度坐标转换成屏幕坐标,更新弹窗位置并显示。
示例代码片段:
// 状态变量 LatLng? tappedCoordinate; String? tappedFeatureId; Offset? popupOffset; // 视图结构 Stack( children: [ MapboxMap( // 原有配置... onFeatureTapped: (id, point, coordinates) async { if (id != null) { // 把经纬度转成屏幕坐标 final screenOffset = await mapboxMapController.convertLatLngToScreenCoordinate(coordinates); setState(() { tappedCoordinate = coordinates; tappedFeatureId = id; popupOffset = screenOffset; }); } }, onCameraMove: () { // 地图移动时隐藏弹窗 setState(() { tappedCoordinate = null; tappedFeatureId = null; popupOffset = null; }); }, ), // 自定义气泡弹窗 if (popupOffset != null && tappedFeatureId != null) Positioned( left: popupOffset!.dx - 60, top: popupOffset!.dy - 70, // 向上偏移避免遮挡点击点 child: Container( width: 120, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4, offset: Offset(0,2))], ), child: Text( 'ID: $tappedFeatureId', textAlign: TextAlign.center, style: const TextStyle(fontSize: 14), ), ), ), ], );
方案2:用原生对话框快速实现
如果不需要气泡样式,直接用Flutter原生showDialog弹出对话框显示ID,代码更简洁:
mapboxMap.onFeatureTapped.add((id, point, coordinates) { if (id != null) { showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('要素信息'), content: Text('ID: $id'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('关闭'), ), ], ), ); } });
内容的提问来源于stack exchange,提问作者sayantan poddar
相关产品推荐
相关产品推荐

