Markers in Flutter Maps (SfMaps)

5 May 2021 / 24 minutes to read

Markers can be used to denote the locations. It is possible to use the built-in symbols or display a custom widget at a specific latitude and longitude on a map.

Adding markers

Shape layer

You can show markers at any position on the map by providing latitude and longitude position to the MapMarker, which is the widget returns from the markerBuilder property.

The markerBuilder callback will be called number of times equal to the value specified in the initialMarkersCount property. The default value of the initialMarkersCount property is null.

late List<Model> _data;
late MapShapeSource _dataSource;

@override
void initState() {
  _data = const <Model>[
    Model('Brazil', -14.235004, -51.92528),
    Model('Germany', 51.16569, 10.451526),
    Model('Australia', -25.274398, 133.775136),
    Model('India', 20.593684, 78.96288),
    Model('Russia', 61.52401, 105.318756)
  ];

  _dataSource = MapShapeSource.asset(
    'assets/world_map.json',
    shapeDataField: 'name',
    dataCount: _data.length,
    primaryValueMapper: (index) => _data[index].country,
  );
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
        child: Padding(
      padding: EdgeInsets.only(left: 15, right: 15),
      child: SfMaps(
        layers: <MapLayer>[
          MapShapeLayer(
            source: _dataSource,
            initialMarkersCount: 5,
            markerBuilder: (BuildContext context, int index) {
              return MapMarker(
                latitude: _data[index].latitude,
                longitude: _data[index].longitude,
                iconColor: Colors.blue,
              );
            },
          ),
        ],
      ),
    )),
  );
}

class Model {
  const Model(this.country, this.latitude, this.longitude);

  final String country;
  final double latitude;
  final double longitude;
}

default marker

NOTE

Tile layer

You can show markers at any position on the map by providing latitude and longitude position to the MapMarker, which is the widget returns from the MapTileLayer.markerBuilder property.

The markerBuilder callback will be called number of times equal to the value specified in the initialMarkersCount property. The default value of the initialMarkersCount property is null.

late List<Model> _data;

@override
void initState() {
  _data = const <Model>[
    Model('Brazil', -14.235004, -51.92528),
    Model('Germany', 51.16569, 10.451526),
    Model('Australia', -25.274398, 133.775136),
    Model('India', 20.593684, 78.96288),
    Model('Russia', 61.52401, 105.318756)
  ];

  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: SfMaps(
        layers: <MapLayer>[
          MapTileLayer(
            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
            initialMarkersCount: 5,
            markerBuilder: (BuildContext context, int index) {
              return MapMarker(
                latitude: _data[index].latitude,
                longitude: _data[index].longitude,
                iconColor: Colors.blue,
              );
            },
          ),
        ],
      ),
    ),
  );
}

class Model {
  const Model(this.country, this.latitude, this.longitude);

  final String country;
  final double latitude;
  final double longitude;
}

Tile layer marker

NOTE

Appearance customization

You can customize the built-in markers appearance using the iconType, iconColor, iconStrokeColor, iconStrokeWidth, and size properties of the MapMarker.

NOTE

  • The default value of the iconType is MapIconType.circle.
  • The default value of the iconStrokeWidth is 1.0.
  • The default value of the iconColor is Colors.blue.
  • The default value of the size is Size(14.0, 14.0).
late List<Model> _data;
late MapShapeSource _dataSource;

@override
void initState() {
  _data = <Model>[
    Model(-14.235004, -51.92528),
    Model(51.16569, 10.451526),
    Model(-25.274398, 133.775136),
    Model(20.593684, 78.96288),
    Model(61.52401, 105.318756)
  ];

  _dataSource = MapShapeSource.asset(
    'assets/world_map.json',
    shapeDataField: 'name',
  );
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
        child: Padding(
      padding: EdgeInsets.only(left: 15, right: 15),
      child: SfMaps(
        layers: <MapLayer>[
          MapShapeLayer(
            source: _dataSource,
            initialMarkersCount: 5,
            markerBuilder: (BuildContext context, int index) {
              return MapMarker(
                latitude: _data[index].latitude,
                longitude: _data[index].longitude,
                iconType: MapIconType.triangle,
                size: Size(18, 18),
                iconColor: Colors.green[200],
                iconStrokeColor: Colors.green[900],
                iconStrokeWidth: 2,
              );
            },
          ),
        ],
      ),
    )),
  );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}

marker customization

Adding custom markers

You can show custom marker using the child property of the MapMarker which returns from the markerBuilder.

late List<Model> _data;
late List<Widget> _iconsList;
late MapShapeSource _dataSource;

@override
void initState() {
  _data = <Model>[
    Model(-14.235004, -51.92528),
    Model(51.16569, 10.451526),
    Model(-25.274398, 133.775136),
    Model(20.593684, 78.96288),
    Model(61.52401, 105.318756)
  ];

  _iconsList = <Widget>[
    Icon(Icons.add_location),
    Icon(Icons.airplanemode_active),
    Icon(Icons.add_alarm),
    Icon(Icons.accessibility_new),
    Icon(Icons.account_balance)
  ];

  _dataSource = MapShapeSource.asset(
    'assets/world_map.json',
    shapeDataField: 'name',
  );
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
        child: Padding(
      padding: EdgeInsets.only(left: 15, right: 15),
      child: SfMaps(
        layers: <MapLayer>[
          MapShapeLayer(
            source: _dataSource,
            initialMarkersCount: 5,
            markerBuilder: (BuildContext context, int index) {
              return MapMarker(
                latitude: _data[index].latitude,
                longitude: _data[index].longitude,
                child: _iconsList[index],
              );
            },
          ),
        ],
      ),
    )),
  );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}

custom marker

Adding markers dynamically

You can add markers dynamically using the insertMarker method. The markerBuilder will be called for the respective index once insertMarker method is called. The controller property of MapShapeLayer has to be set with the new instance of MapShapeLayerController.

Marker will be inserted at the given index if the index value is less than or equal to the current available index and the marker will be added as a last item if the index value is greater than the current available index.

NOTE

You can get the current markers count from MapShapeLayerController.markersCount.

For shape layer

late List<Model> _data;
late MapShapeLayerController _controller;
late MapShapeSource _dataSource;
late Random random;

@override
void initState() {
  _data = <Model>[
    Model(-14.235004, -51.92528),
    Model(51.16569, 10.451526),
    Model(-25.274398, 133.775136),
    Model(20.593684, 78.96288),
    Model(61.52401, 105.318756)
  ];

  _dataSource = MapShapeSource.asset(
    'assets/world_map.json',
    shapeDataField: 'name',
  );
  _controller = MapShapeLayerController();
  random = Random();
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
        child: Container(
      height: 350,
      child: Padding(
        padding: EdgeInsets.only(left: 15, right: 15),
        child: Column(
          children: [
            SfMaps(
              layers: <MapLayer>[
                MapShapeLayer(
                  source: _dataSource,
                  initialMarkersCount: 5,
                  markerBuilder: (BuildContext context, int index) {
                    return MapMarker(
                      latitude: _data[index].latitude,
                      longitude: _data[index].longitude,
                      child: Icon(Icons.add_location),
                    );
                  },
                  controller: _controller,
                ),
              ],
            ),
            ElevatedButton(
              child: Text('Add marker'),
              onPressed: () {
                _data.add(Model(-180 + random.nextInt(360).toDouble(),
                    -55 + random.nextInt(139).toDouble()));
                _controller.insertMarker(5);
              },
            ),
          ],
        ),
      ),
    )),
  );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}

For Tile layer

late List<Model> _data;
late MapTileLayerController _controller;
late Random random;

@override
void initState() {
  _data = <Model>[
    Model(-14.235004, -51.92528),
    Model(51.16569, 10.451526),
    Model(-25.274398, 133.775136),
    Model(20.593684, 78.96288),
    Model(61.52401, 105.318756)
  ];
  _controller = MapTileLayerController();
  random = Random();
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
        child: Container(
      height: 350,
      child: Padding(
        padding: EdgeInsets.only(left: 15, right: 15),
        child: Column(
          children: [
            SfMaps(
              layers: <MapLayer>[
                MapTileLayer(
                  urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                  initialMarkersCount: 5,
                  markerBuilder: (BuildContext context, int index) {
                    return MapMarker(
                      latitude: _data[index].latitude,
                      longitude: _data[index].longitude,
                      child: Icon(Icons.add_location),
                    );
                  },
                  controller: _controller,
                ),
              ],
            ),
            ElevatedButton(
              child: Text('Add marker'),
              onPressed: () {
                _data.add(Model(-180 + random.nextInt(360).toDouble(),
                    -55 + random.nextInt(139).toDouble()));
                _controller.insertMarker(5);
              },
            ),
          ],
        ),
      ),
    )),
  );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}

Add markers dynamically

Updating the existing markers

You can update multiple markers at a same time by passing indices to the updateMarkers method in the MapShapeLayerController. The markerBuilder will be called again for the respective indices once updateMarkers method is called.

NOTE

late List<Model> _data;
late MapShapeLayerController _controller;
late Widget _markerWidget;
late MapShapeSource _dataSource;

@override
void initState() {
    _data = <Model>[
      Model(-14.235004, -51.92528),
      Model(51.16569, 10.451526),
      Model(-25.274398, 133.775136),
      Model(20.593684, 78.96288),
      Model(61.52401, 105.318756)
    ];

    _dataSource = MapShapeSource.asset(
       'assets/world_map.json',
        shapeDataField: 'name',
    );

    _controller = MapShapeLayerController();
    _markerWidget =  Icon(Icons.add_location);
    super.initState();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Container(
            height: 350,
            child: Padding(
              padding: EdgeInsets.only(left: 15, right: 15),
              child: Column(
                children: [
                  SfMaps(
                    layers: <MapLayer>[
                      MapShapeLayer(
                        source: _dataSource,
                        initialMarkersCount: 5,
                        markerBuilder: (BuildContext context, int index){
                          return MapMarker(
                            latitude: _data[index].latitude,
                            longitude: _data[index].longitude,
                            child: _markerWidget,
                          );
                        },
                        controller: _controller,
                      ),
                    ],
                  ),
                  ElevatedButton(
                    child: Text('Update marker'),
                    onPressed: () {
                      List<int> updateList = <int>[1, 2];
                      _markerWidget = Icon(Icons.people);
                      _controller.updateMarkers(updateList);
                    },
                  ),
                ],
              ),
            ),
          )
      ),
   );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}

Update markers dynamically

Deleting a marker

You can remove marker at any index using the removeMarkerAt method.

NOTE

late List<Model> _data;
late MapShapeLayerController _controller;
late MapShapeSource _dataSource;

@override
void initState() {
    _data = <Model>[
      Model(-14.235004, -51.92528),
      Model(51.16569, 10.451526),
      Model(-25.274398, 133.775136),
      Model(20.593684, 78.96288),
      Model(61.52401, 105.318756)
    ];

    _dataSource = MapShapeSource.asset(
       'assets/world_map.json',
       shapeDataField: 'name',
    );
    _controller = MapShapeLayerController();
    super.initState();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Container(
            height: 350,
            child: Padding(
              padding: EdgeInsets.only(left: 15, right: 15),
              child: Column(
                children: [
                  SfMaps(
                    layers: <MapLayer>[
                      MapShapeLayer(
                        source: _dataSource,
                        initialMarkersCount: 5,
                        markerBuilder: (BuildContext context, int index){
                          return MapMarker(
                            latitude: _data[index].latitude,
                            longitude: _data[index].longitude,
                            child: Icon(Icons.add_location),
                          );
                        },
                        controller: _controller,
                      ),
                    ],
                  ),
                  ElevatedButton(
                    child: Text('Remove marker'),
                    onPressed: () {
                      _controller.removeMarkerAt(4);
                    },
                  ),
                ],
              ),
            ),
          )
      ),
   );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}

Remove marker dynamically

Clearing the markers

You can clear all markers using the clearMarkers method.

NOTE

late List<Model> _data;
late MapShapeLayerController _controller;
late MapShapeSource _dataSource;

@override
void initState() {
    _data = <Model>[
      Model(-14.235004, -51.92528),
      Model(51.16569, 10.451526),
      Model(-25.274398, 133.775136),
      Model(20.593684, 78.96288),
      Model(61.52401, 105.318756)
    ];

    _dataSource = MapShapeSource.asset(
       'assets/world_map.json',
        shapeDataField: 'name',
    );
    _controller = MapShapeLayerController();
    super.initState();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Container(
            height: 350,
            child: Padding(
              padding: EdgeInsets.only(left: 15, right: 15),
              child: Column(
                children: [
                  SfMaps(
                    layers: <MapLayer>[
                      MapShapeLayer(
                        source: _dataSource,
                        initialMarkersCount: 5,
                        markerBuilder: (BuildContext context, int index){
                          return MapMarker(
                            latitude: _data[index].latitude,
                            longitude: _data[index].longitude,
                            child: Icon(Icons.add_location),
                          );
                        },
                        controller: _controller,
                      ),
                    ],
                  ),
                  ElevatedButton(
                    child: Text('Clear marker'),
                    onPressed: () {
                      _controller.clearMarkers();
                    },
                  ),
                ],
              ),
            ),
          )
      ),
   );
}

class Model {
  Model(this.latitude, this.longitude);

  final double latitude;
  final double longitude;
}