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

如何为flutter_map中的Marker添加位置更新动画而非瞬移?

实现flutter_map Marker平滑移动动画的方案

核心思路

不要直接替换Marker的坐标,而是在新旧坐标之间通过**线性插值(或缓动插值)**计算中间位置,利用Flutter的动画系统逐步更新Marker的位置,模拟平滑移动效果,避免“瞬移”。

具体实现步骤

1. 定义车辆状态类

创建类来维护车辆的展示位置、目标位置及动画控制器,统一管理每个车辆的动画逻辑:

class Vehicle {
  final String id;
  LatLng currentPosition;
  LatLng targetPosition;
  AnimationController? animationController;
  Animation<double>? animation;

  Vehicle({
    required this.id,
    required this.currentPosition,
    required this.targetPosition,
  });
}

2. 初始化动画控制器

在页面State类中,为每个车辆初始化动画控制器,监听动画进度实时更新当前位置:

late List<Vehicle> vehicles;

@override
void initState() {
  super.initState();
  // 用初始数据初始化车辆列表
  vehicles = initialVehicleData.map((data) {
    final vehicle = Vehicle(
      id: data.id,
      currentPosition: data.position,
      targetPosition: data.position,
    );
    _setupVehicleAnimation(vehicle);
    return vehicle;
  }).toList();
}

void _setupVehicleAnimation(Vehicle vehicle) {
  vehicle.animationController = AnimationController(
    duration: const Duration(seconds: 4), // 动画时长略小于更新间隔5秒,保证衔接自然
    vsync: this,
  );
  vehicle.animation = Tween<double>(begin: 0, end: 1).animate(
    CurvedAnimation(parent: vehicle.animationController!, curve: Curves.easeInOut),
  )..addListener(() {
      // 根据动画进度插值计算当前位置
      final progress = vehicle.animation!.value;
      vehicle.currentPosition = LatLng(
        vehicle.currentPosition.latitude + 
        (vehicle.targetPosition.latitude - vehicle.currentPosition.latitude) * progress,
        vehicle.currentPosition.longitude + 
        (vehicle.targetPosition.longitude - vehicle.currentPosition.longitude) * progress,
      );
      setState(() {});
    });
}

3. 处理位置更新逻辑

当获取到新的车辆坐标时,设置目标位置并启动动画:

void refreshVehiclePositions(List<VehiclePositionUpdate> newUpdates) {
  for (final update in newUpdates) {
    final vehicle = vehicles.firstWhere((v) => v.id == update.id);
    // 将当前位置设为动画起点,目标位置设为新坐标
    vehicle.currentPosition = vehicle.currentPosition;
    vehicle.targetPosition = update.newPosition;
    // 重置并启动动画
    vehicle.animationController?.reset();
    vehicle.animationController?.forward();
  }
}

4. 在flutter_map中渲染Marker

使用车辆的currentPosition渲染Marker,而非直接使用最新的目标坐标:

FlutterMap(
  options: MapOptions(center: initialMapCenter, zoom: 13),
  children: [
    TileLayer(
      urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      subdomains: const ['a', 'b', 'c'],
    ),
    MarkerLayer(
      markers: vehicles.map((vehicle) => Marker(
            point: vehicle.currentPosition,
            width: 40,
            height: 40,
            child: const Icon(Icons.directions_car, color: Colors.red),
          )).toList(),
    ),
  ],
)

额外优化建议

  • 动态调整动画时长:根据两点间的距离计算动画时长,比如设置每秒移动500米,让不同距离的移动速度更真实。
  • 自定义缓动曲线:替换Curves.easeInOut为Curves.linear实现匀速移动,或Curves.decelerate模拟车辆减速效果。
  • 内存泄漏防护:在页面销毁时释放所有动画控制器:
@override
void dispose() {
  for (final vehicle in vehicles) {
    vehicle.animationController?.dispose();
  }
  super.dispose();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:30:03