Flutter GoogleMap:如何在相机移动(缩放)时调整Marker大小
实现Flutter GoogleMap缩放时动态调整Marker大小
核心思路
Marker的尺寸无法直接通过参数修改,只能通过动态生成对应缩放比例的图标,再更新Marker集合来实现缩放效果。具体步骤是:监听地图缩放变化→计算缩放比例→生成对应大小的BitmapDescriptor→更新Marker集合并刷新UI。
具体实现代码
1. 定义控制器逻辑(以GetX为例,StatefulWidget可替换为setState)
首先在控制器中保存Marker原始数据、缩放缓存,实现图标缩放和Marker生成逻辑:
import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:image/image.dart' as img; class MapController extends GetxController { double zoomLevel = 15.0; double currentZoom = 15.0; LatLng latLong = const LatLng(37.7749, -122.4194); GoogleMapController? mapController; // 保存Marker原始数据(包含位置、ID、图标路径) List<MarkerRawData> originalMarkerData = [ MarkerRawData( id: 'marker_1', latLng: const LatLng(37.7749, -122.4194), iconAssetPath: 'assets/marker.png', ), // 其他Marker数据... ]; Set<Marker> jobMarkers = {}; final Map<double, BitmapDescriptor> _iconCache = {}; // 缓存缩放后的图标,避免重复生成 final double baseZoom = 15.0; // 基准缩放级别,对应Marker原始大小 final double scaleFactor = 1.2; // 每缩放1级,尺寸变化的倍数(可根据需求调整) Timer? _debounceTimer; // 防抖计时器,避免频繁触发图标生成 @override void onInit() { super.onInit(); _generateMarkers(zoomLevel); // 初始化生成Marker } // 根据当前缩放级别生成Marker集合 void _generateMarkers(double zoom) async { // 计算缩放比例:基于基准zoom,每增减1级,尺寸乘以scaleFactor的N次方 final double scale = (scaleFactor).pow(zoom - baseZoom).toDouble(); Set<Marker> newMarkers = {}; for (var data in originalMarkerData) { BitmapDescriptor? icon = _iconCache[scale]; // 如果缓存中没有对应缩放的图标,就生成并缓存 if (icon == null) { icon = await _getScaledMarkerIcon(data.iconAssetPath, scale); _iconCache[scale] = icon; } newMarkers.add(Marker( markerId: MarkerId(data.id), position: data.latLng, icon: icon, )); } jobMarkers = newMarkers; update(); // 刷新UI } // 生成指定缩放比例的Marker图标 Future<BitmapDescriptor> _getScaledMarkerIcon(String assetPath, double scale) async { // 加载原始图片 final imageProvider = AssetImage(assetPath); final stream = imageProvider.resolve(const ImageConfiguration()); final completer = Completer<ImageInfo>(); stream.addListener(ImageStreamListener((info, _) => completer.complete(info))); final imageInfo = await completer.future; // 计算缩放后的尺寸 final int targetWidth = (imageInfo.image.width * scale).round(); final int targetHeight = (imageInfo.image.height * scale).round(); // 将图片转为字节流并缩放 final byteData = await imageInfo.image.toByteData(format: ImageByteFormat.png); if (byteData == null) return BitmapDescriptor.defaultMarker; final img.Image? originalImg = img.decodeImage(byteData.buffer.asUint8List()); if (originalImg == null) return BitmapDescriptor.defaultMarker; final img.Image resizedImg = img.copyResize(originalImg, width: targetWidth, height: targetHeight); final Uint8List resizedBytes = img.encodePng(resizedImg); return BitmapDescriptor.fromBytes(resizedBytes); } // 处理地图缩放变化(加防抖避免频繁触发) void changeMarkerSizeOnZoom(CameraPosition position) { currentZoom = position.zoom; _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 100), () { _generateMarkers(currentZoom); }); } // 其他原有方法... void navigateMapToCurrentLocation() { // 你的定位逻辑 } } // 自定义类:保存Marker原始数据 class MarkerRawData { final String id; final LatLng latLng; final String iconAssetPath; MarkerRawData({ required this.id, required this.latLng, required this.iconAssetPath, }); }
2. 修改GoogleMap组件代码
更新onCameraMove回调,调用控制器的缩放处理方法:
GoogleMap( minMaxZoomPreference: MinMaxZoomPreference(null, ctrl.zoomLevel + 4), zoomControlsEnabled: false, zoomGesturesEnabled: true, myLocationButtonEnabled: false, mapType: MapType.normal, onMapCreated: (mapCtrl) { ctrl.mapController = mapCtrl; ctrl.update(); ctrl.navigateMapToCurrentLocation(); }, onCameraMove: (position) { ctrl.changeMarkerSizeOnZoom(position); // 替换原有方法调用 }, initialCameraPosition: CameraPosition( target: ctrl.latLong, zoom: ctrl.zoomLevel, ), markers: ctrl.jobMarkers, circles: ctrl.circles, )
关键优化点
- 图标缓存:缓存不同缩放比例的图标,避免每次缩放都重新生成,提升性能。
- 防抖处理:延迟100ms再生成Marker,避免
onCameraMove频繁触发导致的卡顿。 - 参数可调:通过修改
baseZoom和scaleFactor,可以适配不同的缩放灵敏度需求。
依赖说明
需要在pubspec.yaml中添加图片处理依赖:
dependencies: image: ^4.0.17
内容的提问来源于stack exchange,提问作者priyank apps
相关产品推荐
相关产品推荐

