MarkerComposable is recomposing for every marker in collection
Not sure if this is a bug or an issue on my side, so I'm opening the tiket as Support request.
I migrated my markers to use MarkerComposable instead of Marker with BitmapDescriptor to build more complex icons. I have hundreds of markers shown simultaneously with the same icon. When using Marker this is not an issue, Google Maps is perfectly responsive. After using MarkerComposable the app gets stuck for a few seconds when the markers are shown or updated.
From what I could tell, the icon inside MarkerComposable is being regenerated for every marker even when they have the same key. Here is the simplest sample I could come up with that reproduces the issue:
val singapore = LatLng(1.3588227, 103.8742114)
val singapore2 = LatLng(1.40, 103.77)
val singapore3 = LatLng(1.45, 103.77)
val singapore4 = LatLng(1.50, 103.77)
val singapore5 = LatLng(1.3418, 103.8461)
val singapore6 = LatLng(1.3430, 103.8844)
val singapore7 = LatLng(1.3430, 103.9116)
val singapore8 = LatLng(1.3300, 103.8624)
val singapore9 = LatLng(1.3200, 103.8541)
val singapore10 = LatLng(1.3200, 103.8765)
val defaultCameraPosition = CameraPosition.fromLatLngZoom(singapore, 11f)
val points =
listOf(singapore, singapore2, singapore3, singapore4, singapore5, singapore6, singapore7, singapore8, singapore9, singapore10)
GoogleMap(
cameraPositionState = rememberCameraPositionState { position = defaultCameraPosition },
) {
points.forEach { point ->
MarkerComposable(
keys = arrayOf("key"),
state = rememberUpdatedMarkerState(point),
content = { MarkerContent() }
)
}
}
@Composable
private fun MarkerContent() {
Log.d(TAG, "Composing marker")
Box(
modifier = Modifier
.width(88.dp)
.height(36.dp)
.clip(RoundedCornerShape(16.dp))
.background(Color.Red),
contentAlignment = Alignment.Center,
) {
Text(
text = "Compose Marker",
textAlign = TextAlign.Center,
)
}
}
In the logcat you can see that the content composable was called multiple times:
The only way I could figure out to fix it was to make rememberComposeBitmapDescriptor public to generate the icon outside of my forEach loop and use a regular Marker.
val icon = rememberComposeBitmapDescriptor("singapore") { MarkerContent() }
points.forEach { point ->
Marker(
state = rememberUpdatedMarkerState(point),
icon = icon,
)
}
So my question is, am I using MarkerComposable wrong? I could not find anything in the docs or samples to suggest so.
Tested with versions 4.3.3 and 6.5.0 on Android.
You've got a list so can you wrap each of the markercomposable within a key() block.. so that only when key changes, your MarkerComposable would need to recompose.
Hi @Sloy,
Your usage of the keys parameter is right, but the issue here lies on the content lamba. The keys parameter in a MarkerComposable is primarily for helping Compose's reconciliation process when the list of markers itself changes (e.g., adding or removing markers). It doesn't, in this context, serve as a caching mechanism for the content of individual markers if that content is generated anew in each loop iteration. This is something that could be potentially done. Not sure if hashing the content lambda, because this could be a non trivial task. Or arguably making rememberComposeBitmapDescriptor public, since we already have a bunch of remember functions available.
TL;DR: your usage is correct with the current API. This is something that could be also improved from the library. I am leaving the issue open.
I would honestly love to have rememberComposeBitmapDescriptor be public. Having control over something as heavy as rendering & bitmap generation is always going to be a good thing. I wouldn't mind it having to be @OptIn/@Experimental if you fear that the implementation might change.