How to Add Animated and Interactive Custom Map Markers in Flutter Maps
Tags: flutter, mobile, android, webdev
Published: 2021-05-11 12:18:18

---
title: How to Add Animated and Interactive Custom Map Markers in Flutter Maps
published: true
date: 2021-05-11 11:00:51 UTC
tags: flutter, mobile, android, webdev
canonical_url: https://www.syncfusion.com/blogs/post/add-animated-and-interactive-custom-map-markers-in-flutter-maps.aspx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtawewr3mn9v5y6ch3pw.png
---

In Maps, markers are a primary tool used to denote locations with precise latitude and longitude coordinates. The Syncfusion [Flutter Maps](https://www.syncfusion.com/flutter-widgets/flutter-maps "Link to Flutter Maps widget") widget has built-in support to add markers on the [MapShapeLayer](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapShapeLayer-class.html "Link to MapShapeLayer class") or [MapTileLayer](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapTileLayer-class.html "Link to MapTileLayer class"). You can use any type of custom widget as a marker, or built-in shapes such as circles, diamonds, squares, and triangles.

In this blog, I am going to explain how to add markers and animate them. If you are new to our Flutter Maps widget, please refer to the following blog posts before proceeding:

- [Introducing Flutter Maps Widget](https://www.syncfusion.com/blogs/post/introducing-flutter-maps-widget.aspx "Link to Introducing Flutter Maps Widget blog") [Blog]
- [Easily Visualize OpenStreetMaps and Bing Maps in Flutter](https://dev.to/syncfusion/easily-visualize-openstreetmaps-and-bing-maps-in-flutter-1j46 "Link to Easily Visualize OpenStreetMaps and Bing Maps in Flutter blog") [Blog]

## **Add an animated marker at load time**

To add an animated marker at load time, we have to add an animated widget as a child of [MapMarker](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapMarker-class.html "Link to MapMarker class") and start the animation in the [initState](https://api.flutter.dev/flutter/widgets/State/initState.html "Link to initState method").

In the following example, I’ve animated the fourth index marker at load time.

```dart
class _LoadAnimatableMarkerAtLoadtime extends StatefulWidget {
  @override
  _LoadAnimatableMarkerAtLoadtimeState createState() =>
      _LoadAnimatableMarkerAtLoadtimeState();
}

class _LoadAnimatableMarkerAtLoadtimeState
    extends State<_LoadAnimatableMarkerAtLoadtime>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late CurvedAnimation _animation;
  late Map<String, MapLatLng> _markers;

  int _selectedMarkerIndex = 4;

  @override
  void initState() {
    _controller = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 750));
    _animation =
        CurvedAnimation(parent: _controller, curve: Curves.easeOutBack);

    _markers = <String, MapLatLng>{
      'Chad': MapLatLng(15.454166, 18.732206),
      'Nigeria': MapLatLng(9.081999, 8.675277),
      'DRC': MapLatLng(-4.038333, 21.758663),
      'CAR': MapLatLng(6.600281, 20.480205),
      'Sudan': MapLatLng(12.862807, 30.217636),
      'Kenya': MapLatLng(0.0236, 37.9062),
      'Zambia': MapLatLng(-10.974129, 30.861397),
      'Egypt': MapLatLng(25.174109, 28.776359),
      'Algeria': MapLatLng(24.276672, 7.308186),
    };
    _controller.repeat(min: 0.15, max: 1.0, reverse: true);
    super.initState();
  }

  @override
  void dispose() {
    _controller.stop();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Load animatable marker at loadtime'),
      ),
      body: MapTileLayer(
        urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
        initialZoomLevel: 3,
        initialFocalLatLng: MapLatLng(2.3104, 16.5581),
        initialMarkersCount: _markers.length,
        markerBuilder: (BuildContext context, int index) {
          final double size = _selectedMarkerIndex == index ? 40 : 25;
          final MapLatLng markerData = _markers.values.elementAt(index);
          final Icon current = Icon(Icons.location_pin, size: size);
          return MapMarker(
            latitude: markerData.latitude,
            longitude: markerData.longitude,
            child: Transform.translate(
              offset: Offset(0.0, -size / 2),
              child: _selectedMarkerIndex == index
                  ? ScaleTransition(
                      alignment: Alignment.bottomCenter,
                      scale: _animation,
                      child: current)
                  : current,
            ),
          );
        },
      ),
    );
  }
}
```

![Adding an Animated Marker During Initial Loading of Flutter Maps](https://www.syncfusion.com/blogs/wp-content/uploads/2021/05/Adding-an-Animated-Marker-During-Initial-Loading-of-Flutter-Maps.gif)<figcaption>Adding an Animated Marker During Initial Loading of Flutter Maps</figcaption>

## **Adding a marker programmatically**

To add or update markers dynamically, we will need the help of [MapShapeLayerController](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapShapeLayerController-class.html "Link to MapShapeLayerController class") or [MapTileLayerController](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapTileLayerController-class.html "Link to MapTileLayerController class") depending on whether we use [MapShapeLayer](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapShapeLayer-class.html "Link to MapShapeLayer class") or [MapTileLayer](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapTileLayer-class.html "Link to MapTileLayer class") to include the marker. MapShapeLayerController and MapTileLayerController control the marker collection dynamically by adding, removing, updating, and clearing the markers.

In the following example, we will add a marker dynamically at a precise coordinate by using the [insertMarker](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapLayerController/insertMarker.html "Link to insertMarker method") method of [MapShapeLayerController](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapShapeLayerController-class.html "Link to MapShapeLayerController class"). Here, we use the [Icon](https://api.flutter.dev/flutter/widgets/Icon-class.html "Link to Icon class") widget as a marker. Calling the [insertMarker](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapLayerController/insertMarker.html "Link to insertMarker method") method of the MapShapeLayerControl triggers the [TileLayer](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapTileLayer-class.html "Link to MapTileLayer class")’s [markerBuilder](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapLayer/markerBuilder.html "Link to markerBuilder property") callback with the respective index. Here, we have to return the [MapMarker](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapMarker-class.html "Link to MapMarker class") to be updated in the current index.

```dart
class AddingMarkerDynamically extends StatefulWidget {
  @override
  _AddingMarkerDynamicallyState createState() =>
      _AddingMarkerDynamicallyState();
}

class _AddingMarkerDynamicallyState extends State
    with SingleTickerProviderStateMixin {
  late MapTileLayerController _tileLayerController;

  @override
  void initState() {
    _tileLayerController = MapTileLayerController();
    super.initState();
  }

  @override
  void dispose() {
    _tileLayerController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Add a marker dynamically'),
      ),
      body: MapTileLayer(
        urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
        initialZoomLevel: 3,
        initialFocalLatLng: MapLatLng(2.3104, 16.5581),
        controller: _tileLayerController,
        markerBuilder: (BuildContext context, int index) {
          return MapMarker(
            latitude: 6.6111,
            longitude: 20.9394,
            child: Transform.translate(
              offset: Offset(0.0, -20.0),
              child: Icon(Icons.location_pin, size: 40),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add_location),
        onPressed: () => _tileLayerController.insertMarker(0),
      ),
    );
  }
}
```

![Adding an Animated Marker Dynamically in Flutter Maps](https://www.syncfusion.com/blogs/wp-content/uploads/2021/05/Adding-an-Animated-Marker-Dynamically-in-Flutter-Maps.gif)<figcaption>Adding an Animated Marker Dynamically in Flutter Maps</figcaption>

## **Animate a marker or group of markers dynamically**

Dynamic marker updates require the help of [MapShapeLayerController](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapShapeLayerController-class.html "Link to MapShapeLayerController class") and [MapTileLayerController](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapTileLayerController-class.html "Link to MapTileLayerController class"). The following example demonstrates updating [MapMarker](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapMarker-class.html "Link to MapMarker class")’s child dynamically as an animated widget to start the animation.

In parallel, invoke [updateMarkers](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapLayerController/updateMarkers.html "Link to updateMarkers method") with respective marker indices to update the markers.

```dart
class AnimateGroupOfMarkersDynamically extends StatefulWidget {
  @override
  _AnimateGroupOfMarkersDynamicallyState createState() =>
      _AnimateGroupOfMarkersDynamicallyState();
}

class _AnimateGroupOfMarkersDynamicallyState
    extends State
    with TickerProviderStateMixin {
  late AnimationController _controller;
  late CurvedAnimation _animation;
  late MapTileLayerController _tileLayerController;
  late Map<String, MapLatLng> _markers;

  List _selectedMarkerIndices = [];

  @override
  void initState() {
    _controller = AnimationController(
        vsync: this,
        duration: const Duration(milliseconds: 750),
        reverseDuration: const Duration(milliseconds: 750));
    _animation =
        CurvedAnimation(parent: _controller, curve: Curves.easeOutBack);
    _tileLayerController = MapTileLayerController();

    _markers = <String, MapLatLng>{
      'Chad': MapLatLng(15.454166, 18.732206),
      'Nigeria': MapLatLng(9.081999, 8.675277),
      'DRC': MapLatLng(-4.038333, 21.758663),
      'CAR': MapLatLng(6.600281, 20.480205),
      'Sudan': MapLatLng(12.862807, 30.217636),
      'Kenya': MapLatLng(0.0236, 37.9062),
      'Zambia': MapLatLng(-10.974129, 30.861397),
      'Egypt': MapLatLng(25.174109, 28.776359),
      'Algeria': MapLatLng(24.276672, 7.308186),
    };

    _controller.repeat(min: 0.1, max: 1.0, reverse: true);
    super.initState();
  }

  @override
  void dispose() {
    _controller.stop();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Animate group of markers dynamically')),
      body: MapTileLayer(
        urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
        initialZoomLevel: 3,
        initialFocalLatLng: MapLatLng(2.3104, 16.5581),
        controller: _tileLayerController,
        initialMarkersCount: _markers.length,
        markerBuilder: (BuildContext context, int index) {
          final double size = _selectedMarkerIndices.contains(index) ? 40 : 25;
          final MapLatLng markerLatLng = _markers.values.elementAt(index);
          Widget current = Icon(Icons.location_pin, size: size);
          return MapMarker(
            latitude: markerLatLng.latitude,
            longitude: markerLatLng.longitude,
            child: Transform.translate(
              offset: Offset(0.0, -size / 2),
              child: _selectedMarkerIndices.contains(index)
                  ? ScaleTransition(
                      alignment: Alignment.bottomCenter,
                      scale: _animation,
                      child: current)
                  : current,
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.animation),
        onPressed: () {
          _selectedMarkerIndices = [0, 2, 4];
          _tileLayerController.updateMarkers(_selectedMarkerIndices);
          _controller.repeat(min: 0.1, max: 1.0, reverse: true);
        },
      ),
    );
  }
}
```

![Animating Markers Dynamically in Futter Maps](https://www.syncfusion.com/blogs/wp-content/uploads/2021/05/Animating-Markers-Dynamically-in-Futter-Maps.gif)<figcaption>Animating Markers Dynamically in Futter Maps</figcaption>

## **Animate a marker on interaction**

Assume we have a group of markers in which we are going to animate a single marker on a tap or click gesture.

For this, we need to wrap the marker’s child inside the [GestureDetector](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html "Link to GestureDetector class") and use the [GestureDetector.onTap](https://api.flutter.dev/flutter/widgets/GestureDetector/onTap.html "Link to onTap property") callback to handle the tap interaction. On tapping, call [updateMarkers](https://pub.dev/documentation/syncfusion_flutter_maps/latest/maps/MapLayerController/updateMarkers.html "Link to updateMarkers method") with the marker indices to animate it.

The following code demonstrates this.

```dart
int _selectedMarkerIndex = -1;
int _prevSelectedMarkerIndex = -1;

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Animate a single marker dynamically'),
    ),
    body: MapTileLayer(
      urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
      initialZoomLevel: 3,
      initialFocalLatLng: MapLatLng(2.3104, 16.5581),
      controller: _tileLayerController,
      initialMarkersCount: _markers.length,
      markerBuilder: (BuildContext context, int index) =>
          _buildMarker(context, index),
    ),
  );
}

MapMarker _buildMarker(BuildContext context, int index) {
  final double size = _selectedMarkerIndex == index ? 40 : 25;
  final MapLatLng markerLatLng = _markers.values.elementAt(index);
  final Icon current = Icon(Icons.location_pin);
  return MapMarker(
    latitude: markerLatLng.latitude,
    longitude: markerLatLng.longitude,
    child: GestureDetector(
      onTap: () {
        _prevSelectedMarkerIndex = _selectedMarkerIndex;
        _selectedMarkerIndex = index;
        _tileLayerController.updateMarkers([
          if (_prevSelectedMarkerIndex != -1) _prevSelectedMarkerIndex,
          _selectedMarkerIndex
        ]);
      },
      child: AnimatedContainer(
        transform: Matrix4.identity()..translate(0.0, -size / 2),
        duration: const Duration(milliseconds: 250),
        height: size,
        width: size,
        child: FittedBox(child: current),
      ),
    ),
  );
}
```

![Animate a Marker on Interaction in Flutter Maps](https://www.syncfusion.com/blogs/wp-content/uploads/2021/05/Animate-a-Marker-on-Interaction-in-Flutter-Maps.gif)<figcaption>Animate a Marker on Interaction in Flutter Maps</figcaption>

## **Resource**

For more information, refer to this [animating markers on Flutter Maps project](https://github.com/SyncfusionExamples/flutter-maps-marker-animation "Link to Add and animate a marker using Flutter maps demo on GitHub") on GitHub.

## **Conclusion**

In this blog, we’ve learned how to animate a single marker and groups of markers at load time, dynamically, and also via interaction in the Syncfusion [Flutter Maps](https://www.syncfusion.com/flutter-widgets/flutter-maps "Link to Flutter Maps") widget. Give it a try and leave your feedback in the comments section below.

Check out other features of our Flutter Maps in the [user guide](https://help.syncfusion.com/flutter/maps/ "Link to Flutter Maps documentation") and explore our [Flutter Maps project samples](https://github.com/syncfusion/flutter-examples "Link to Flutter examples on GitHub"). Additionally, check out our demo apps available on different platforms: [Android](https://play.google.com/store/apps/details?id=com.syncfusion.flutter.examples "Link to Flutter demos on Google Play Store"), [iOS](https://apps.apple.com/us/app/syncfusion-flutter-ui-widgets/id1475231341 "Link to Flutter demos on App Store"), [web](https://flutter.syncfusion.com/?_ga=2.113248516.2088510317.1618203864-1079363253.1592211341 "Link to Flutter online demos"), [Windows](https://www.microsoft.com/store/productId/9NHNBWCSF85D "Link to Flutter demos on Microsoft Store"), and [Linux](https://snapcraft.io/syncfusion-flutter-gallery "Link to Flutter demos on Snapcraft").

If you need a new widget for the Flutter framework or new features in our existing widgets, you can contact us through our [support forums](https://www.syncfusion.com/forums "Link to the Syncfusion support forum"), [Direct-Trac](https://www.syncfusion.com/support/directtrac/incidents "Link to the Syncfusion support system Direct Trac"), or [feedback portal](https://www.syncfusion.com/feedback/flutter "Link to Syncfusion Feedback Portal"). As always, we are happy to assist you!

If you like this blog post, we think you will also like the following articles too:

- [How to Add Location Tracking to Your App Using Syncfusion Flutter Maps](https://dev.to/syncfusion/how-to-add-location-tracking-to-your-app-using-syncfusion-flutter-maps-34jd "Link to How to Add Live Location Tracking to Your App Using Syncfusion Flutter Maps blog") [Blog]
- [How to Display Routes and Highlight Regions in Syncfusion Flutter Maps](https://dev.to/syncfusion/how-to-display-routes-and-highlight-regions-in-syncfusion-flutter-maps-5afn "Link to How to Display Routes and Highlight Regions in Syncfusion Flutter Maps blog") [Blog]
- [How to Create a Choropleth Map in a Flutter Application](https://dev.to/syncfusion/how-to-create-a-choropleth-map-in-a-flutter-application-2bl6 "Link to How to Create a Choropleth Map in a Flutter Application blog") [Blog]