How to Cluster Markers on Google Maps using Flutter
By: António Valente
Tags: Marker, Google Maps, Software Development, Flutter, Clustering

Fluster Package Comes to the Rescue

In your search for solutions, you probably bumped into the Fluster package. This package uses a Dart port of supercluster and works really well, but how do you use it on your Flutter project?

The readme is very brief, doesn’t explain much and the example isn’t even a Flutter project. This makes the package very easy to overlook unless you find this demo project by the package author, but even with this code, you will struggle to understand how to use it in your project. At least I did. Again, there are no proper readme instructions and the code can be a bit overwhelming at first.

In this article, I will break down the code and explain it step by step so you can understand and implement a similar solution in your project.

Implementing Fluster

1. Extending the Clusterable class

Fluster can only handle markers that conform to the Clusterable abstract class so this is a good place to start.

import 'package:fluster/fluster.dart';

import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'package:meta/meta.dart'; class MapMarker extends Clusterable {

final String id;

final LatLng position;

final BitmapDescriptor icon; MapMarker({

@required this.id,

@required this.position,

@required this.icon,

isCluster = false,

clusterId,

pointsSize,

childMarkerId,

}) : super(

markerId: id,

latitude: position.latitude,

longitude: position.longitude,

isCluster: isCluster,

clusterId: clusterId,

pointsSize: pointsSize,

childMarkerId: childMarkerId,

); Marker toMarker() => Marker(

markerId: MarkerId(id),

position: LatLng(

position.latitude,

position.longitude,

),

icon: icon,

);

}

Here is my implementation. You can see it on Github here. Notice how I’ve added the toMarkert() method. This method will come in handy later on, to convert our list of MapMarkers to a list of Markers so our GoogleMap widget doesn’t complain.

Note that you can add more arguments to the class depending on your use case. Like an onTap() callback or a Marker InfoWindow.

2. Create the List of Markers to be Displayed

import 'package:flutter_google_maps_clusters/helpers/map_marker.dart';

import 'package:google_maps_flutter/google_maps_flutter.dart'; final List<MapMarker> markers = []; final List<LatLng> markerLocations = [

LatLng(41.147125, -8.611249),

LatLng(41.145599, -8.610691),

...

]; for (LatLng markerLocation in _markerLocations) {

markers.add(

MapMarker(

id: markerLocations.indexOf(markerLocation).toString(),

position: markerLocation,

icon: markerImage,

),

);

}

You should have a list of locations where you want to place the markers. Create a MapMarker for every location.

3. Init Fluster

import 'package:fluster/fluster.dart';

import 'package:flutter_google_maps_clusters/helpers/map_marker.dart'; final Fluster<MapMarker> fluster = Fluster<MapMarker>(

minZoom: minZoom, // The min zoom at clusters will show

maxZoom: maxZoom, // The max zoom at clusters will show

radius: 150, // Cluster radius in pixels

extent: 2048, // Tile extent. Radius is calculated with it.

nodeSize: 64, // Size of the KD-tree leaf node.

points: markers, // The list of markers created before

createCluster: ( // Create cluster marker

BaseCluster cluster,

double lng,

double lat,

) => MapMarker(

id: cluster.id.toString(),

position: LatLng(lat, lng),

icon: clusterImage,

isCluster: cluster.isCluster,

clusterId: cluster.id,

pointsSize: cluster.pointsSize,

childMarkerId: cluster.childMarkerId,

),

);

Here, you’re creating your Fluster instance. It will handle all the logic of updating the markers and clusters you need to show in your map for a given zoom level. For more information about the parameters used, check out the Fluster source code or the supercluster options.

4. Get Cluster Markers

import 'package:fluster/fluster.dart';

import 'package:flutter_google_maps_clusters/helpers/map_marker.dart';

import 'package:google_maps_flutter/google_maps_flutter.dart'; final List<Marker> googleMarkers = fluster

.clusters([-180, -85, 180, 85], currentZoom)

.map((cluster) => cluster.toMarker())

.toList()

Now you’ll see that handy toMarker() method coming to action. We’re telling fluster to get the cluster markers within the giving bounding box of [westLng, southLat, eastLng, northLat] for the current zoom level and converting them to Google Maps Markers. Now we can use that list to show the markers on the map.

5. Show the Map with Markers

import 'package:flutter/material.dart';

import 'package:google_maps_flutter/google_maps_flutter.dart'; @override

Widget build(BuildContext context) {

return Scaffold(

body: GoogleMap(

initialCameraPosition: CameraPosition(

target: LatLng(41.143029, -8.611274),

zoom: currentZoom,

),

markers: googleMarkers.toSet(),

onMapCreated: (controller) {

// You can init Fluster here

},

onCameraMove: (position) {

// Update the markers for the updated position.zoom

),

),

);

}

In the build(context) method of your page, you should create your Google Map widget and pass the markers created by fluster and voilà! You will have clusters showing in your map. 👏 👏 👏

You can use the onMapCreated() to know when the map is ready to show your markers. Also, make sure to update the markers with the updated zoom level on the onCameraMove() by calling setState() or using your favorite state management system, so Fluster knows if it needs to cluster or uncluster some markers.