Google Maps in Jetpack Compose: Markers

In a few recent projects, I’ve needed to utilise Google Maps within environments utilising Jetpack Compose. In the early days of Compose this felt light a sought-after piece of functionality – even though it is still being built on, it now seems to be in a place where I can confidently use it. In this series of blog posts, I’ll share how we can use the different parts of the Compose mapping package.

Now that we have the basics down from the previous post, we’re going to dive into the Marker composable and learn how we can show markers on our map.


Looking to learn more Jetpack Compose? The video course for Practical Jetpack Compose is now available 🚀


The Marker Composable

When it comes to displaying markers on our composable map, the Marker composable provides this functionality for our Google Maps instance. It’s important to note the @GoogleMapComposable annotation – we covered in the previous post that the GoogleMap composable only supports children using this annotation.

@Composable
@GoogleMapComposable
public fun Marker(
    state: MarkerState = rememberMarkerState(),
    contentDescription: String? = "",
    alpha: Float = 1.0f,
    anchor: Offset = Offset(0.5f, 1.0f),
    draggable: Boolean = false,
    flat: Boolean = false,
    icon: BitmapDescriptor? = null,
    infoWindowAnchor: Offset = Offset(0.5f, 0.0f),
    rotation: Float = 0.0f,
    snippet: String? = null,
    tag: Any? = null,
    title: String? = null,
    visible: Boolean = true,
    zIndex: Float = 0.0f,
    onClick: (Marker) -> Boolean = { false },
    onInfoWindowClick: (Marker) -> Unit = {},
    onInfoWindowClose: (Marker) -> Unit = {},
    onInfoWindowLongClick: (Marker) -> Unit = {},
)

As we can see from this composable, there is a collection of arguments that allow us to customise our Marker. To be able to display the Marker at our chosen location, we’re going to need to utilise the MarkerState argument. This allows us to provide a LatLng for where the Marker should be displayed at.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))

We can then assign this MarkerState reference using the state argument of the Marker composable.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker
    )
}

With this in place, we’ll now be able to see a Marker displayed on our map.

As we can see, this doesn’t give much information to our users. We can assign a title to our Marker using the title argument, which takes string value to be displayed in a popup when the Marker is clicked.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        title = "Home"
    )
}

When clicking the Marker, we can now see a Title is displayed within the popup window.

We can go one step further here and also provide a string value for the snippet argument, this is where we can provide some additional text to be displayed beneath the title. Currently to use the snippet, this must be used alongside the title, otherwise the popup window will not be displayed.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        title = "Home",
        snippet = "London"
    )
}

Handling Marker Appearance

Alongside displaying text information, we can also utilise the icon argument to customise the appearance of the marker. For example, we can use the BitmapDescriptorFactory and its defaultMarker function to instantiate a marker using a specified hue.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        title = "Home",
        snippet = "London",
        icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)
    )
}

This will then alter the color being used for our marker, based on the provided hue value.

The icon argument takes a BitmapDescriptor reference, so we can provide some other kind of decoration if we are looking to do more than modify the color. For example, let’s provide a drawable to be used for our marker. Here, we’ll create a Bitmap from a drawable reference, providing this to the fromBitmap function of the BitmapDescriptorFactory. We can then provide this to the icon argument of our Marker composable.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
val customIcon = BitmapFactory.decodeResource(LocalContext.current.resources, R.drawable.marker)
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        title = "Home",
        snippet = "London",
        icon = BitmapDescriptorFactory.fromBitmap(customIcon)
    )
}

With this in place we can now see our custom marker icon being used when the Marker is drawn on the map.

In some cases, markers may be disabled from interactions or ‘inactive’ within our map. When it comes to these scenarios we can use the alpha argument of the Marker composable to control the alpha of the drawn marker.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        alpha = 0.4f
    )
}

We can see how this modifies the appearance of our marker when drawn on our map.


Handling Marker Interactions

Controlling the appearance of our markers is one thing, but we may also want to handle any interaction events that occur. While clicking on the marker currently displays a popup window, we may want to handle this interaction differently – such as showing our own popup alert, or displaying some kind of different UI to the user. In these scenarios, we can intercept click events on a marker by using the onClick argument of the Marker composable. Within this handler, we can trigger our own way of dealing with the click event. One important thing to note here is the boolean value that is returned from the lambda – to fully intercept the event we can return true, this will prevent the popup window from showing after this click event has taken place.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        onClick = { marker ->
            showAlert = true
            true
        }
    )
}

With this click handler, we will now be able to intercept click events and handle the marker interaction in our own way.

If we still wish to utilise the info window, we can also intercept click events on this popup using the onInfoWindowClick argument. This can be helpful if we want to show additional information based on the information window, or navigate the user to another part of our UI.

val marker = rememberMarkerState(position = LatLng(51.5072, 0.1276))
GoogleMap(
   ...
) {
    Marker(
        state = marker,
        onInfoWindowClick = { marker ->
            showAlert = true
        }
    )
}

In this blog post, we’ve taken a look at the marker composable, understanding how we can use it to portray map information to our users, as well as customise its appearance and behaviour. In the following posts, we’ll start to look at customising our map further through other composables that are supported through the GoogleMapComposable content scope.