Skip to content

Animated Annotations

Smoothly animate annotation position changes using Flutter's animation system.

How it works

Annotation positions are updated via updateSymbol(). By driving updates from a Flutter AnimationController, you get smooth motion at the Flutter tick rate (typically 60fps).

Animate a symbol position

late AnimationController _animationController;
late Animation<double> _animation;
Symbol? _symbol;

@override
void initState() {
  super.initState();
  _animationController = AnimationController(
    duration: const Duration(seconds: 2),
    vsync: this,
  );
  _animation = CurvedAnimation(
    parent: _animationController,
    curve: Curves.easeInOut,
  );
}

void _startAnimation(LatLng from, LatLng to) {
  _animation.addListener(() {
    final t = _animation.value;
    final lat = from.latitude + (to.latitude - from.latitude) * t;
    final lng = from.longitude + (to.longitude - from.longitude) * t;
    controller.updateSymbol(
      _symbol!,
      SymbolOptions(geometry: LatLng(lat, lng)),
    );
  });
  _animationController.forward(from: 0);
}

Or using lerpDouble from dart:ui:

_animationController.addListener(() {
  final lat = lerpDouble(start.latitude, end.latitude, _animation.value)!;
  final lng = lerpDouble(start.longitude, end.longitude, _animation.value)!;
  controller.updateSymbol(
    symbol,
    SymbolOptions(geometry: LatLng(lat, lng)),
  );
});
_animationController.forward();

Animate bearing (icon rotation)

_animationController.addListener(() {
  final bearing = lerpDouble(startBearing, endBearing, _animation.value)!;
  controller.updateSymbol(
    symbol,
    SymbolOptions(iconRotate: bearing),
  );
});

This is useful for animated vehicle icons that rotate to face the direction of travel.

Continuous tracking animation

For real-time tracking (GPS updates), restart the animation on each new position:

void onNewPosition(LatLng newPos) {
  final fromPos = _currentPos;
  _currentPos = newPos;
  _animationController.value = 0;
  _from = fromPos;
  _to = newPos;
  _animationController.forward();
}

Performance

updateSymbol() triggers a style update on every frame. For very fast animations or many symbols simultaneously, consider switching to Style Layers with setGeoJsonFeature(), which batches updates more efficiently.

Key APIs