You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为Flutter google_maps_flutter的Marker实现长按、按下等额外手势?

如何在google_maps_flutter的Marker上实现长按、按下等手势?

我之前也碰到过这个问题——google_maps_flutter的Marker组件默认只提供了onTap点击事件,要实现长按(onLongPress)、按下(tap down)这类手势,得通过一些变通的方法来实现,下面分享两种实用的方案:

方案一:利用GoogleMap的全局手势回调结合坐标判断

这个方法的核心思路是:借助GoogleMap组件本身的全局手势回调(比如onLongPressonTapDown),拿到触发手势的坐标后,判断该坐标是否落在某个Marker的"触发范围"内,从而关联到对应的Marker执行逻辑。

步骤说明:

  1. 先把所有Marker的信息(比如markerIdposition)存储在一个可遍历的集合里(比如Map<MarkerId, Marker>);
  2. 给GoogleMap组件添加对应手势的全局回调;
  3. 在回调中遍历Marker集合,计算触发手势的坐标与每个Marker坐标的距离,设置一个合理的阈值(比如50米,可根据需求调整),当距离小于阈值时,就判定为触发了该Marker的手势;
  4. 执行对应的业务逻辑。

代码示例:

import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart'; // 用来计算经纬度距离

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  final Map<MarkerId, Marker> _markers = {};
  GoogleMapController? _mapController;

  @override
  void initState() {
    super.initState();
    // 初始化添加一个Marker
    final MarkerId markerId = MarkerId('1');
    final Marker marker = Marker(
      markerId: markerId,
      position: LatLng(25.032970, 121.565415),
      // 记得设置consumeTapEvents: true,避免和地图手势冲突
      consumeTapEvents: true,
      onTap: () {
        print('Marker 1 被点击了');
      },
    );
    setState(() {
      _markers[markerId] = marker;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GoogleMap(
        initialCameraPosition: CameraPosition(
          target: LatLng(25.032970, 121.565415),
          zoom: 15,
        ),
        markers: Set<Marker>.of(_markers.values),
        onMapCreated: (controller) => _mapController = controller,
        // 实现Marker长按逻辑
        onLongPress: (LatLng pressPosition) {
          _markers.forEach((markerId, marker) {
            // 计算长按位置与Marker位置的距离(单位:米)
            double distance = Geolocator.distanceBetween(
              marker.position.latitude,
              marker.position.longitude,
              pressPosition.latitude,
              pressPosition.longitude,
            );
            // 设置触发阈值,这里设为50米
            if (distance < 50) {
              print('Marker ${markerId.value} 被长按了');
              // 这里执行你的长按业务逻辑
            }
          });
        },
        // 实现Marker按下(tap down)逻辑
        onTapDown: (TapDownDetails details) {
          // 把屏幕坐标转换为经纬度坐标
          LatLng? pressPosition = _mapController?.getLatLng(details.localPosition);
          if (pressPosition != null) {
            _markers.forEach((markerId, marker) {
              double distance = Geolocator.distanceBetween(
                marker.position.latitude,
                marker.position.longitude,
                pressPosition!.latitude,
                pressPosition.longitude,
              );
              if (distance < 50) {
                print('Marker ${markerId.value} 被按下了');
                // 这里执行你的按下业务逻辑
              }
            });
          }
        },
      ),
    );
  }
}

方案二:使用自定义Overlay覆盖Marker(进阶)

如果对精度要求更高,或者需要更复杂的手势交互,可以通过Flutter的Overlay组件在Marker上方叠加一个透明的GestureDetector,直接捕获手势事件。不过这个方法需要处理Overlay与Marker位置的同步,实现起来稍复杂:

  1. 监听地图的相机移动事件,实时更新Overlay的位置,使其始终与Marker对齐;
  2. 在Overlay中添加GestureDetector,设置onLongPressonTapDown等回调;
  3. 确保Overlay的大小和Marker的点击范围匹配。

这个方案适合需要高度自定义手势的场景,但日常需求用方案一基本足够。

注意事项:

  • 阈值的设置要根据地图的缩放级别调整,缩放越大,阈值可以设得越小;
  • 引入geolocator库是为了方便计算经纬度距离,你也可以自己实现距离计算逻辑;
  • 一定要给Marker设置consumeTapEvents: true,避免Marker的onTap和地图的全局手势冲突。

内容的提问来源于stack exchange,提问作者郭奇靂

火山引擎 最新活动