Flutter Google Maps如何同时显示所有标记的信息窗口?
在Flutter Google Maps中同时显示所有标记的信息窗口
默认情况下,Flutter的Google Maps插件不支持同时显示多个Marker的Info Window——插件本身的设计就是每次只能展示一个Info Window,当你通过循环调用marker.showInfoWindow()时,后续的调用会自动关闭之前打开的窗口,最终只保留最后一个。
要实现同时显示所有信息窗口的效果,你需要放弃使用Marker自带的Info Window,改用自定义Info Window组件,将其作为独立Widget叠加在地图上方,具体实现如下:
实现思路
- 用
Stack组件将Google Maps和自定义Info Window放在同一层级; - 通过
GoogleMapController把每个Marker的经纬度转换为屏幕坐标; - 根据转换后的坐标定位自定义Info Window的位置;
- 监听地图相机移动事件(缩放、拖动),实时更新Info Window的位置,保证坐标匹配。
代码示例
import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; class MultiInfoWindowMap extends StatefulWidget { const MultiInfoWindowMap({super.key}); @override State<MultiInfoWindowMap> createState() => _MultiInfoWindowMapState(); } class _MultiInfoWindowMapState extends State<MultiInfoWindowMap> { late GoogleMapController _mapController; final List<Marker> _markers = [ Marker( markerId: const MarkerId('marker1'), position: const LatLng(37.7749, -122.4194), ), Marker( markerId: const MarkerId('marker2'), position: const LatLng(37.7895, -122.401), ), // 可添加更多标记 ]; final Map<MarkerId, Offset> _markerScreenOffsets = {}; @override Widget build(BuildContext context) { return Stack( children: [ GoogleMap( initialCameraPosition: const CameraPosition( target: LatLng(37.7749, -122.4194), zoom: 12, ), markers: Set.of(_markers), onMapCreated: (controller) { _mapController = controller; _updateMarkerScreenPositions(); }, onCameraMove: (_) => _updateMarkerScreenPositions(), ), // 渲染所有自定义Info Window ..._markers.map((marker) { final offset = _markerScreenOffsets[marker.markerId]; if (offset == null) return const SizedBox.shrink(); // 调整偏移让窗口显示在标记上方 final adjustedOffset = Offset(offset.dx, offset.dy - 70); return Positioned( left: adjustedOffset.dx, top: adjustedOffset.dy, child: _CustomInfoWindow( title: '标记 ${marker.markerId.value}', snippet: '自定义信息窗口内容', ), ); }), ], ); } // 计算标记对应的屏幕坐标 void _updateMarkerScreenPositions() async { final offsets = <MarkerId, Offset>{}; for (final marker in _markers) { final screenCoord = await _mapController.getScreenCoordinate(marker.position); offsets[marker.markerId] = Offset( screenCoord.x.toDouble(), screenCoord.y.toDouble(), ); } if (mounted) { setState(() => _markerScreenOffsets.addAll(offsets)); } } } // 自定义Info Window样式组件 class _CustomInfoWindow extends StatelessWidget { final String title; final String snippet; const _CustomInfoWindow({required this.title, required this.snippet}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 4), Text(snippet, style: const TextStyle(fontSize: 12)), ], ), ); } }
注意事项
- 自定义Info Window的样式可完全自定义,适配不同UI需求;
- 标记数量过多时需注意性能,避免大量Widget渲染导致卡顿;
- 如需给Info Window添加交互(比如点击事件),直接在自定义组件中添加即可。
内容的提问来源于stack exchange,提问作者user9733254
相关产品推荐
相关产品推荐

