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

Mapbox Flutter中flyTo/easeTo动画失效,setCamera可正常工作

问题排查与解决方案

核心问题定位

你遇到的flyTo/easeTo失效问题,大概率和两个关键因素有关:build方法中重复注册监听器、硬编码的cameraOptions覆盖相机状态。


解决方案1:将ref.listen移出build方法

在build方法中调用ref.listen会导致每次Widget重建时都注册新的监听器,不仅浪费资源,还可能引发异步操作的冲突(比如多次触发flyTo,或者动画执行时Widget状态不稳定)。正确的做法是在初始化阶段注册监听器,并在销毁时清理:

如果是ConsumerStatefulWidget:

late final ListenerSubscription _listener;

@override
void initState() {
  super.initState();
  // 在initState中注册监听器
  _listener = ref.listen(focusedAnnotationProvider, (_, focusedAnnotation) {
    if (focusedAnnotation != null) {
      _animateToAnnotation(focusedAnnotation).then((_) {
        if (mounted) {
          _showPopup(focusedAnnotation);
          ref.read(focusedAnnotationProvider.notifier).state = null;
        }
      });
    }
  });
}

@override
void dispose() {
  // 销毁监听器,避免内存泄漏
  _listener.close();
  super.dispose();
}

如果是HookWidget:

useEffect(() {
  final listener = ref.listen(focusedAnnotationProvider, (_, focusedAnnotation) {
    if (focusedAnnotation != null) {
      _animateToAnnotation(focusedAnnotation).then((_) {
        if (mounted) {
          _showPopup(focusedAnnotation);
          ref.read(focusedAnnotationProvider.notifier).state = null;
        }
      });
    }
  });
  // 组件销毁时清理监听器
  return listener.close;
}, []); // 空依赖列表确保只注册一次

解决方案2:移除MapWidget中硬编码的cameraOptions

你当前在build方法中给MapWidget设置了固定的cameraOptions,这会导致每次Widget重建时,地图都会强制重置到初始位置。如果ref.listen的回调触发时刚好遇到Widget重建,flyTo的动画会被直接打断,相机位置被重置回初始值,看起来就像动画完全没执行。

修改方式:将初始相机位置的设置移到onMapCreated回调中,而非build的cameraOptions参数:

MapWidget(
  key: const ValueKey("pinPalsWidget"),
  // 移除硬编码的cameraOptions
  onMapCreated: (mapboxMap) {
    _onMapCreated(mapboxMap);
    // 在这里设置初始相机位置
    mapboxMap.setCamera(CameraOptions(
      center: Point(coordinates: Position(-72.2896, 43.7028)),
      zoom: 10.0,
    ));
  },
  // 其他参数保持不变
  ...
)

额外验证步骤

  1. 在_animateToAnnotation中添加日志,确认mapboxMap实例有效,以及flyTo执行前后的相机位置:
Future<void> _animateToAnnotation(LocationAnnotation annotation) async {
  if (mapboxMap case final mapboxMap?) {
    // 打印当前相机位置
    final currentCamera = await mapboxMap.getCameraState();
    print("Current camera: ${currentCamera.center}, zoom: ${currentCamera.zoom}");

    double long = annotation.coordinates.coordinates.lng.toDouble();
    double lat = annotation.coordinates.coordinates.lat.toDouble();
    await mapboxMap.flyTo(
      CameraOptions(
        center: Point(
          coordinates: Position(long, lat),
        ),
        zoom: 15.0,
      ),
      MapAnimationOptions(duration: 2000, startDelay: 0),
    );

    // 打印执行后的相机位置
    final newCamera = await mapboxMap.getCameraState();
    print("New camera: ${newCamera.center}, zoom: ${newCamera.zoom}");
  }
}

如果日志显示相机位置确实变化了,但UI没更新,说明是Widget重建导致的重置;如果位置没变化,再检查是否有其他代码在修改相机状态。

  1. 确认没有其他相机操作冲突:比如位置跟踪服务是否在持续调用setCamera或flyTo,覆盖了你触发的动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:07:28