如何在google_maps_flutter中实现双指固定中心缩放+单指平移+整数缩放
Flutter GoogleMap 单双指操作分离与整数缩放实现
要实现你需求的三个功能,我们需要自定义手势逻辑并控制相机缩放级别,以下是具体实现方案:
一、分离单双指操作逻辑
先禁用GoogleMap默认的拖动和缩放手势,通过外层GestureDetector单独处理单指拖动和双指缩放:
- 在
GoogleMap组件中添加属性,关闭默认手势:scrollGesturesEnabled: false, zoomGesturesEnabled: false, - 用
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
相关产品推荐
相关产品推荐

