如何在Flutter Syncfusion Map中编程移动Marker至目标MapLatLng
实现Syncfusion Flutter Map中蓝色Marker向红色目标Marker的平滑移动效果
核心思路
通过Flutter的AnimationController和Tween对经纬度进行插值计算,实时更新蓝色Marker的位置,模拟行进的平滑动画效果。因为Syncfusion的MapLatLng是不可变类,需要自定义插值逻辑来实现经纬度的逐步过渡。
步骤实现
1. 基础状态与动画初始化
在State类中定义必要的变量,包括当前位置、目标位置、动画控制器以及经纬度的动画插值对象:
import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_maps/maps.dart'; class MapMarkerMovementDemo extends StatefulWidget { const MapMarkerMovementDemo({super.key}); @override State<MapMarkerMovementDemo> createState() => _MapMarkerMovementDemoState(); } class _MapMarkerMovementDemoState extends State<MapMarkerMovementDemo> with SingleTickerProviderStateMixin { // 初始蓝色Marker位置(模拟用户当前位置) late MapLatLng _currentPosition; // 红色目标Marker位置 MapLatLng? _targetPosition; // 动画控制器 late AnimationController _animationController; // 纬度、经度的动画插值 late Animation<double> _latAnimation; late Animation<double> _lngAnimation; @override void initState() { super.initState(); // 初始化模拟位置(示例为北京天安门附近) _currentPosition = const MapLatLng(39.9042, 116.4074); // 初始化动画控制器,时长设为3秒(可按需调整) _animationController = AnimationController( vsync: this, duration: const Duration(seconds: 3), ); } @override void dispose() { _animationController.dispose(); super.dispose(); } }
2. 处理地图点击事件,触发移动动画
为SfMaps添加onTap回调,获取用户点击的经纬度作为目标位置,然后初始化并启动动画:
// 用户点击地图时触发 void _onMapTap(MapLatLng tappedPosition) { setState(() { _targetPosition = tappedPosition; }); // 重置动画避免冲突 _animationController.reset(); // 创建纬度、经度的Tween插值 _latAnimation = Tween<double>( begin: _currentPosition.latitude, end: _targetPosition!.latitude, ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); _lngAnimation = Tween<double>( begin: _currentPosition.longitude, end: _targetPosition!.longitude, ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); // 监听动画更新,实时刷新当前位置 _animationController.addListener(() { setState(() { _currentPosition = MapLatLng( _latAnimation.value, _lngAnimation.value, ); }); }); // 动画结束后,将当前位置固定到目标位置 _animationController.addStatusListener((status) { if (status == AnimationStatus.completed) { setState(() { _currentPosition = _targetPosition!; }); } }); _animationController.forward(); }
3. 构建带Marker的地图UI
在build方法中创建SfMaps组件,添加MarkerLayer渲染蓝色当前位置Marker和红色目标Marker:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Marker Movement Demo')), body: SfMaps( layers: [ MapTileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', initialZoomLevel: 14, initialFocalLatLng: _currentPosition, onTap: _onMapTap, markerBuilder: (BuildContext context, int index) { final markers = <MapMarker>[]; // 添加蓝色当前位置Marker markers.add(MapMarker( latitude: _currentPosition.latitude, longitude: _currentPosition.longitude, child: const Icon(Icons.location_on, color: Colors.blue, size: 30), )); // 有目标位置时添加红色目标Marker if (_targetPosition != null) { markers.add(MapMarker( latitude: _targetPosition!.latitude, longitude: _targetPosition!.longitude, child: const Icon(Icons.location_pin, color: Colors.red, size: 30), )); } return markers[index]; }, markerCount: _targetPosition != null ? 2 : 1, ), ], ), ); }
关键细节说明
- 动画曲线:使用
Curves.easeInOut实现自然的加速减速效果,替换为Curves.linear可实现匀速移动。 - 速度调整:修改
AnimationController的duration参数可控制移动快慢。 - 重复点击处理:每次点击前调用
_animationController.reset(),避免动画叠加冲突。 - 地图瓦片:示例使用OpenStreetMap瓦片,可替换为Syncfusion支持的其他地图服务瓦片。
内容的提问来源于stack exchange,提问作者Abdurraheem Abdulhakeem
相关产品推荐
相关产品推荐

