animated_route_marker 0.1.0
animated_route_marker: ^0.1.0 copied to clipboard
Moves a google_maps_flutter marker smoothly along a route or towards live GPS positions at a real speed, with smooth bearing, play/pause/seek and camera follow.
animated_route_marker #
Moves a google_maps_flutter marker (courier, taxi, truck, …)
smoothly along a route at a real speed, or towards live GPS positions, and turns it towards the direction of
travel without sudden spins.
Features #
- Route playback at a constant speed in m/s, or over a total duration. Distances and headings are computed on the great circle (haversine, initial bearing, spherical interpolation), so long segments and the antimeridian work too.
- Smooth heading: the marker eases into new headings and always turns the short way (359° → 1° is a 2° turn).
- Live mode:
animateTo(position)glides from where the marker is to each new location fix. A fix that arrives mid-way continues from the current point. GPS noise below a threshold is ignored, and a single far-off "jump" is ignored until consecutive fixes confirm it. - Controls:
play,pause,seek,seekToDistance,stop,loop, changingspeedwhile moving, anonCompletedcallback, progress as aValueListenable<RouteProgress>and as aStream<RouteProgress>. - Camera follow: keep the map centered on the marker, optionally with zoom, tilt and map rotation.
- Travelled route in a different color (optional): the route is split into a passed and a remaining polyline.
- Many markers at once: one controller per marker, one
AnimatedRouteMarkerswidget for all of them. - No leaks: each controller uses a
Tickerfrom thevsyncyou pass (likeAnimationController) and releases it indispose. Tests check that disposing a screen mid-animation leaves no ticker running. - Plain inputs: the API takes
List<LatLng>, so routes can come from any source (Directions API, your backend, GPX, a recorded trip) and icons from anyBitmapDescriptor.
How it differs from existing packages #
Checked on pub.dev on 22 October 2026:
| Package | What it does | What is missing for route animation |
|---|---|---|
flutter_animarker 3.2.0 |
Animates a marker through a list of positions, with rotation and ripple | Last release April 2021, repository inactive since early 2024; open issues about markers no longer animating and rotation taking the long way past 180° |
animated_marker 0.3.2 |
Animates position and rotation changes between two states | No route, speed, play/pause/seek or camera follow |
marker_motion 0.2.0 |
Animates between two positions by marker id | Its README lists rotation, polyline snapping and a minimum-distance threshold as not done yet |
google_map_animation 0.0.2 |
Draws polylines progressively, fades markers in | Marker movement is not tied to a route or a speed |
animated_route_marker is built for the courier/taxi case: a speed in meters per second, shortest-way heading
easing, a GPS jump filter for live data, playback controls, camera follow and a travelled/remaining route split,
for several markers at once.
Installation #
dependencies:
animated_route_marker: ^0.1.0
google_maps_flutter: ^2.13.0
Usage #
Moving along a route #
import 'package:animated_route_marker/animated_route_marker.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class CourierMap extends StatefulWidget {
const CourierMap({super.key, required this.route});
final List<LatLng> route;
@override
State<CourierMap> createState() => _CourierMapState();
}
class _CourierMapState extends State<CourierMap> with TickerProviderStateMixin {
late final AnimatedRouteMarkerController courier = AnimatedRouteMarkerController(
vsync: this,
// Icon, anchor, callbacks…: everything except position and rotation is kept.
marker: const Marker(markerId: MarkerId('courier'), flat: true, anchor: Offset(0.5, 0.5)),
polylineStyle: const RoutePolylineStyle(), // optional: passed part in grey
onCompleted: () => debugPrint('Arrived'),
)..setRoute(widget.route, speed: 12, autoPlay: true); // 12 m/s ≈ 43 km/h
@override
void dispose() {
courier.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedRouteMarkers(
controllers: [courier],
builder: (context, markers, polylines) => GoogleMap(
initialCameraPosition: CameraPosition(target: widget.route.first, zoom: 15),
markers: markers,
polylines: polylines,
),
);
}
}
Use flat: true and anchor: Offset(0.5, 0.5) so the icon turns around its center and with the map. If your icon
does not point north, set rotationOffset (for an icon pointing east: -90).
Instead of a speed you can give the trip duration: setRoute(points, duration: Duration(minutes: 3)).
Controls and progress #
courier.play();
courier.pause();
courier.seek(0.5); // half way
courier.seekToDistance(1200); // 1.2 km from the start
courier.stop(); // back to the start
courier.speed = 25; // m/s, also while moving
courier.loop = true;
ValueListenableBuilder<RouteProgress>(
valueListenable: courier,
builder: (context, progress, _) => Slider(value: progress.fraction, onChanged: courier.seek),
);
courier.progressStream.listen((p) => print('${p.distance} / ${p.totalDistance} m, ${p.status}'));
RouteProgress holds the position, the displayed bearing, distance, totalDistance, remainingDistance,
fraction, segmentIndex, status (idle, playing, paused, completed) and mode (route, live).
Live positions #
Call animateTo whenever a new location arrives, for example from
advanced_background_locator or any location stream:
final vehicle = AnimatedRouteMarkerController(
vsync: this,
marker: const Marker(markerId: MarkerId('taxi'), flat: true, anchor: Offset(0.5, 0.5)),
liveDuration: const Duration(seconds: 1), // ≈ your location update interval
minMoveDistance: 2, // ignore GPS noise below 2 m (no jitter or spinning while parked)
jumpDistance: 500, // a fix > 500 m away is a GPS jump…
jumpConfirmations: 2, // …applied only when 2 consecutive fixes agree
);
locationStream.listen((location) {
final accepted = vehicle.animateTo(LatLng(location.latitude, location.longitude));
// accepted == false: ignored as noise or as an unconfirmed jump.
});
The first position places the marker directly; later ones are interpolated from the current point. Use
jumpTo(position) to place the marker without animation.
Camera follow #
GoogleMap(onMapCreated: (map) => courier.followCamera(map, zoom: 16));
// Navigation style: tilted, map turns with the marker.
courier.followCamera(map, zoom: 17, tilt: 45, rotate: true);
courier.stopFollowingCamera();
Without zoom, tilt and rotate, only the camera target changes and the user keeps their zoom. Camera moves are
sent one at a time; frames that arrive while the map is busy are merged.
Several markers #
Create one controller per vehicle (all with the same vsync from a TickerProviderStateMixin) and pass them all
to AnimatedRouteMarkers. Add your static markers in the builder: markers: {...markers, ...myMarkers}.
Routes from google_maps_polyline, icons from marker_crafter #
The package has no dependency on them, but they fit together (see example/):
import 'package:google_maps_polyline/google_maps_polyline.dart';
import 'package:marker_crafter/marker_crafter.dart';
// Route: from the Directions API, or decode an encoded polyline.
final result = await GoogleMapsPolyline().getRouteBetweenCoordinates(
apiKey,
const MyPointLatLng(41.0222, 28.9770),
const MyPointLatLng(41.0035, 28.9500),
);
final route = [for (final p in result.points) LatLng(p.latitude, p.longitude)];
// or: PolylineCodec.decode(encoded)
// Icon: a canvas-drawn marker that points north.
final icon = await createLocationMarkerBitmap(Colors.indigo, size: 34);
courier.markerTemplate = courier.markerTemplate.copyWith(iconParam: icon);
courier.setRoute(route, speed: 12, autoPlay: true);
Geometry helpers #
GeoMath.distance, GeoMath.bearing, GeoMath.interpolate, GeoMath.shortestAngleDelta and
GeoMath.lerpAngle are pure functions; RoutePath gives the length of a polyline and the point and heading at
any distance along it.
Example app #
example/ shows three vehicles on decoded routes with playback controls, and a live tab fed by a simulated GPS
with noise and a "GPS jump" button. It is in English and Turkish.
The Google Maps key is read from MAPS_API_KEY in example/android/local.properties (not committed):
MAPS_API_KEY=your-key
Without a key the example still opens and runs the same animation on a simple map-less preview.
License #
MIT
Türkçe #
google_maps_flutter üzerindeki bir marker'ı (kurye, taksi, kamyon…) bir rota boyunca gerçek hızla ya da canlı GPS
konumlarına doğru akıcı biçimde hareket ettirir ve yönünü ani dönüşler olmadan hareket yönüne çevirir.
Özellikler
- Rota oynatma: sabit hız (m/s) ya da toplam süre ile. Mesafe ve yön büyük daire üzerinde hesaplanır (haversine, başlangıç yönü, küresel interpolasyon).
- Yumuşak dönüş: marker yeni yöne kademeli döner ve her zaman kısa yoldan döner (359° → 1° = 2°).
- Canlı mod:
animateTo(konum)her yeni konumda mevcut noktadan yenisine kayar. Eşik altındaki GPS gürültüsü yok sayılır; tek bir uzak "sıçrama" ardışık konumlarla doğrulanana kadar uygulanmaz. - Kontrol:
play,pause,seek,seekToDistance,stop,loop, hareket halindeykenspeeddeğişimi,onCompleted,ValueListenable<RouteProgress>veStream<RouteProgress>ile ilerleme. - Kamera kilidi:
followCamera(map, zoom: 16); isteğe bağlı eğim ve haritanın marker ile dönmesi. - Geçilen yol farklı renkte (isteğe bağlı) ve aynı anda birden fazla marker (
AnimatedRouteMarkers). - Sızıntı yok: her controller verdiğiniz
vsync'ten birTickeralır vedispose'da bırakır. - Çekirdek API
List<LatLng>alır;google_maps_polylinevemarker_crafteryalnızca example'da kullanılır.
Mevcut paketlerden farkı (22 Ekim 2026'da pub.dev'de incelendi): flutter_animarker Nisan 2021'den beri
güncellenmiyor ve açık hata kayıtları var (animasyonun durması, 180°'de uzun yoldan dönme). animated_marker ve
marker_motion iki konum arasında animasyon yapar; rota, hız (m/s), oynat/duraklat/ileri sar ve kamera takibi yok.
animated_route_marker kurye/taksi senaryosu için hız, kısa yoldan dönüş, GPS sıçrama filtresi, oynatma
kontrolleri, kamera kilidi ve geçilen/kalan rota ayrımını birlikte sunar.
Kısa kullanım
final kurye = AnimatedRouteMarkerController(
vsync: this, // TickerProviderStateMixin
marker: const Marker(markerId: MarkerId('kurye'), flat: true, anchor: Offset(0.5, 0.5)),
polylineStyle: const RoutePolylineStyle(),
onCompleted: () => debugPrint('Vardı'),
)..setRoute(rota, speed: 12, autoPlay: true);
AnimatedRouteMarkers(
controllers: [kurye],
builder: (context, markers, polylines) => GoogleMap(
initialCameraPosition: baslangic,
markers: markers,
polylines: polylines,
),
);
// Canlı konum:
konumAkisi.listen((k) => kurye.animateTo(LatLng(k.latitude, k.longitude)));
// dispose içinde:
kurye.dispose();
Örnek uygulama: example/ Türkçe ve İngilizcedir. Harita anahtarı example/android/local.properties
dosyasındaki MAPS_API_KEY değerinden okunur; anahtar yoksa uygulama çökmez ve aynı animasyonu haritasız bir
önizlemede gösterir.
Lisans #
MIT