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

如何在google_maps_flutter中实现双指固定中心缩放+单指平移+整数缩放

Flutter GoogleMap 单双指操作分离与整数缩放实现

要实现你需求的三个功能,我们需要自定义手势逻辑并控制相机缩放级别,以下是具体实现方案:

一、分离单双指操作逻辑

先禁用GoogleMap默认的拖动和缩放手势,通过外层GestureDetector单独处理单指拖动和双指缩放:

  1. 在GoogleMap组件中添加属性,关闭默认手势:
    scrollGesturesEnabled: false,
    zoomGesturesEnabled: false,
    
  2. 用GestureDetector包裹GoogleMap,分别处理两种手势:
    • 单指拖动:通过onPanUpdate获取偏移量,调用地图控制器移动相机
    • 双指缩放:通过缩放回调获取比例,保持屏幕中心不变的同时调整缩放级别

二、整数缩放级别两种实现方式

方式1:触摸时允许小数缩放,松开后自动取整

在双指缩放结束时,将当前缩放级别取整并修正到限定范围内:

onScaleEnd: (details) async {
  final controller = await _mapController.future;
  final currentCamera = await controller.getCameraPosition();
  final roundedZoom = currentCamera.zoom.roundToDouble();
  final clampedZoom = roundedZoom.clamp(6.0, 18.0);
  await controller.moveCamera(
    CameraUpdate.newCameraPosition(
      CameraPosition(target: currentCamera.target, zoom: clampedZoom),
    ),
  );
},

方式2:实时强制取整缩放

在双指缩放过程中,直接将计算后的缩放级别取整,实时更新相机位置:

onScaleUpdate: (details) async {
  final controller = await _mapController.future;
  final currentCamera = await controller.getCameraPosition();
  final newZoom = currentCamera.zoom * details.scale;
  final roundedZoom = newZoom.roundToDouble().clamp(6.0, 18.0);
  
  // 获取屏幕中心对应的经纬度,保持缩放中心固定
  final screenCenter = Offset(
    MediaQuery.of(context).size.width / 2,
    MediaQuery.of(context).size.height / 2,
  );
  final centerLatLng = await controller.getLatLng(screenCenter);
  
  await controller.moveCamera(
    CameraUpdate.newCameraPosition(
      CameraPosition(target: centerLatLng, zoom: roundedZoom),
    ),
  );
},

完整代码示例

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapScreen extends StatefulWidget {
  const MapScreen({super.key});

  @override
  State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  final Completer<GoogleMapController> _mapController = Completer();
  final LatLng _center = const LatLng(36, 128);
  double _zoom = 10.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        // 单指拖动地图
        onPanUpdate: (details) async {
          final controller = await _mapController.future;
          await controller.moveCamera(
            CameraUpdate.scrollBy(-details.delta.dx, -details.delta.dy),
          );
        },
        // 方式2:实时取整缩放(启用此方式请注释下方onScaleEnd)
        onScaleUpdate: (details) async {
          final controller = await _mapController.future;
          final currentCamera = await controller.getCameraPosition();
          final newZoom = currentCamera.zoom * details.scale;
          final roundedZoom = newZoom.roundToDouble().clamp(6.0, 18.0);
          
          final screenCenter = Offset(
            MediaQuery.of(context).size.width / 2,
            MediaQuery.of(context).size.height / 2,
          );
          final centerLatLng = await controller.getLatLng(screenCenter);
          
          await controller.moveCamera(
            CameraUpdate.newCameraPosition(
              CameraPosition(target: centerLatLng, zoom: roundedZoom),
            ),
          );
        },
        // 方式1:松开后取整缩放(启用此方式请注释上方onScaleUpdate)
        /*
        onScaleEnd: (details) async {
          final controller = await _mapController.future;
          final currentCamera = await controller.getCameraPosition();
          final roundedZoom = currentCamera.zoom.roundToDouble().clamp(6.0, 18.0);
          await controller.moveCamera(
            CameraUpdate.newCameraPosition(
              CameraPosition(target: currentCamera.target, zoom: clampedZoom),
            ),
          );
        },
        */
        child: GoogleMap(
          initialCameraPosition: CameraPosition(
            target: _center,
            zoom: _zoom,
          ),
          onMapCreated: (controller) {
            _mapController.complete(controller);
          },
          mapToolbarEnabled: false,
          cameraTargetBounds: CameraTargetBounds(LatLngBounds(
            southwest: const LatLng(33, 124),
            northeast: const LatLng(39, 132),
          )),
          minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
          rotateGesturesEnabled: false,
          zoomControlsEnabled: false,
          tiltGesturesEnabled: false,
          myLocationButtonEnabled: false,
          scrollGesturesEnabled: false,
          zoomGesturesEnabled: false,
        ),
      ),
    );
  }
}

注意事项

  • 两种缩放方式二选一,不要同时启用,避免逻辑冲突
  • 双指缩放时保持屏幕中心固定依赖getLatLng方法,需确保地图已初始化完成
  • 确认项目已配置好Google Maps API密钥,避免地图加载失败

内容的提问来源于stack exchange,提问作者Durian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 02:05:35