You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 14:00:39