googlemaps / android-maps-compose

Jetpack Compose composables for the Maps SDK for Android
https://developers.google.com/maps/documentation/android-sdk/maps-compose
Apache License 2.0
1.16k stars 143 forks source link

Using Marker or MarkerComposable in Clustering #641

Open feivur opened 3 weeks ago

feivur commented 3 weeks ago

Is your feature request related to a problem? Please describe. Now we cannot use Marker or MarkerComposable in blocks clusterContent and clusterItemContent of Clustering (com.google.maps.android.compose.clustering.Clustering) Therefore, it is not possible to drag and drop custom markers.

Describe the solution you'd like I want to use features together: custom markers, dragging and clustering.

MarkerComposableinside Clustering leads to a crash: case 1 java.lang.IllegalStateException: The ComposeView was measured to have a width or height of zero. Make sure that the content has a non-zero size.

Marker inside Clustering leads to a crash: case 2 java.lang.IllegalStateException: Invalid applier

maps-compose version is 6.1.2

Case 1

@Composable
private fun CustomUiClustering(items: List<MyItem>) {
    Clustering(
        items = items,
        clusterItemContent = {
            MarkerComposable(
                state = rememberMarkerState(position = it.itemPosition)
            ) {
                Text(
                    modifier = Modifier.size(40.dp),
                    text = "test",
                )
            }
        }
    )
}
FATAL EXCEPTION: main
                     Process: com.google.maps.android.compose, PID: 28881
                     java.lang.IllegalStateException: The ComposeView was measured to have a width or height of zero. Make sure that the content has a non-zero size.
                        at com.google.maps.android.compose.RememberComposeBitmapDescriptorKt.renderComposableToBitmapDescriptor(RememberComposeBitmapDescriptor.kt:58)
                        at com.google.maps.android.compose.RememberComposeBitmapDescriptorKt.access$renderComposableToBitmapDescriptor(RememberComposeBitmapDescriptor.kt:1)
                        at com.google.maps.android.compose.RememberComposeBitmapDescriptorKt.rememberComposeBitmapDescriptor(RememberComposeBitmapDescriptor.kt:29)
                        at com.google.maps.android.compose.MarkerKt.MarkerComposable-Khg_OnI(Marker.kt:313)
                        at com.google.maps.android.compose.markerexamples.ComposableSingletons$MarkerClusteringActivityKt$lambda-3$1.invoke(MarkerClusteringActivity.kt:172)
                        at com.google.maps.android.compose.markerexamples.ComposableSingletons$MarkerClusteringActivityKt$lambda-3$1.invoke(MarkerClusteringActivity.kt:171)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer$createAndAddView$view$2.invoke(ClusterRenderer.kt:95)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer$createAndAddView$view$2.invoke(ClusterRenderer.kt:95)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer$InvalidatingComposeView.Content(ClusterRenderer.kt:220)
                        at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:259)
                        at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:258)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:380)
                        at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:216)
                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:132)
                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:131)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:380)
                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:121)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:155)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:154)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:401)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:154)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:133)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:97)
                        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3595)
                        at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3522)
                        at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:743)
                        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1122)
                        at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3876)
                        at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:649)
                        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:635)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:124)
                        at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1631)
                        at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:124)
                        at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:180)
                        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.jvm.kt:320)
                        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.jvm.kt:198)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:124)
                        at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1712)
                        at android.view.View.dispatchAttachedToWindow(View.java:23244)
                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3722)
                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3729)
                        at android.view.ViewGroup.addViewInner(ViewGroup.java:5604)
                        at android.view.ViewGroup.addView(ViewGroup.java:5352)
                        at android.view.ViewGroup.addView(ViewGroup.java:5292)
                        at android.view.ViewGroup.addView(ViewGroup.java:5264)
                        at com.google.maps.android.compose.MapComposeViewRenderKt.startRenderingComposeView(MapComposeViewRender.kt:46)
                        at com.google.maps.android.compose.MapComposeViewRenderKt$rememberComposeUiViewRenderer$1$1.startRenderingView(MapComposeViewRender.kt:92)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer.createAndAddView(ClusterRenderer.kt:99)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer.onClustersChanged(ClusterRenderer.kt:67)
                        at com.google.maps.android.clustering.ClusterManager$ClusterTask.onPostExecute(ClusterManager.java:322)
                        at com.google.maps.android.clustering.ClusterManager$ClusterTask.onPostExecute(ClusterManager.java:308)
                        at android.os.AsyncTask.finish(AsyncTask.java:771)
                        at android.os.AsyncTask.-$$Nest$mfinish(Unknown Source:0)
                        at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
                        at android.os.Handler.dispatchMessage(Handler.java:106)
                        at android.os.Looper.loopOnce(Looper.java:230)
                        at android.os.Looper.loop(Looper.java:319)
                        at android.app.ActivityThread.main(ActivityThread.java:9063)
                        at java.lang.reflect.Method.invoke(Native Method)
                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

Case 2

@Composable
private fun CustomUiClustering(items: List<MyItem>) {
    Clustering(
        items = items,
        clusterItemContent = {
            Marker(
                state = rememberMarkerState(position = it.itemPosition)
            )
        }
    )
}
FATAL EXCEPTION: main
                     Process: com.google.maps.android.compose, PID: 32440
                     java.lang.IllegalStateException: Invalid applier
                        at androidx.compose.runtime.ComposablesKt.invalidApplier(Composables.kt:476)
                        at com.google.maps.android.compose.MarkerKt.MarkerImpl-khPtz74(Marker.kt:944)
                        at com.google.maps.android.compose.MarkerKt.Marker-qld6geY(Marker.kt:238)
                        at com.google.maps.android.compose.markerexamples.ComposableSingletons$MarkerClusteringActivityKt$lambda-2$1.invoke(MarkerClusteringActivity.kt:166)
                        at com.google.maps.android.compose.markerexamples.ComposableSingletons$MarkerClusteringActivityKt$lambda-2$1.invoke(MarkerClusteringActivity.kt:165)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer$createAndAddView$view$2.invoke(ClusterRenderer.kt:95)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer$createAndAddView$view$2.invoke(ClusterRenderer.kt:95)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer$InvalidatingComposeView.Content(ClusterRenderer.kt:220)
                        at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:259)
                        at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:258)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:380)
                        at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:216)
                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:132)
                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:131)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:380)
                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:121)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:155)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:154)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:401)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:154)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:133)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                        at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:97)
                        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3595)
                        at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3522)
                        at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:743)
                        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1122)
                        at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3876)
                        at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:649)
                        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:635)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:124)
                        at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1631)
                        at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:124)
                        at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:180)
                        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.jvm.kt:320)
                        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.jvm.kt:198)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:124)
                        at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1712)
                        at android.view.View.dispatchAttachedToWindow(View.java:23244)
                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3722)
                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3729)
                        at android.view.ViewGroup.addViewInner(ViewGroup.java:5604)
                        at android.view.ViewGroup.addView(ViewGroup.java:5352)
                        at android.view.ViewGroup.addView(ViewGroup.java:5292)
                        at android.view.ViewGroup.addView(ViewGroup.java:5264)
                        at com.google.maps.android.compose.MapComposeViewRenderKt.startRenderingComposeView(MapComposeViewRender.kt:46)
                        at com.google.maps.android.compose.MapComposeViewRenderKt$rememberComposeUiViewRenderer$1$1.startRenderingView(MapComposeViewRender.kt:92)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer.createAndAddView(ClusterRenderer.kt:99)
                        at com.google.maps.android.compose.clustering.ComposeUiClusterRenderer.onClustersChanged(ClusterRenderer.kt:67)
                        at com.google.maps.android.clustering.ClusterManager$ClusterTask.onPostExecute(ClusterManager.java:322)
                        at com.google.maps.android.clustering.ClusterManager$ClusterTask.onPostExecute(ClusterManager.java:308)
                        at android.os.AsyncTask.finish(AsyncTask.java:771)
                        at android.os.AsyncTask.-$$Nest$mfinish(Unknown Source:0)
                        at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
                        at android.os.Handler.dispatchMessage(Handler.java:106)
                        at android.os.Looper.loopOnce(Looper.java:230)
                        at android.os.Looper.loop(Looper.java:319)
                        at android.app.ActivityThread.main(ActivityThread.java:9063)
                        at java.lang.reflect.Method.invoke(Native Method)
                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
ln-12 commented 1 hour ago

We are also experiencing case 1. This issue seems to be a follow up to this issue/PR/comment.

I our case it also happens without clustering (also on v6.1.2):

MyGoogleMap(
    cameraPositionState = cameraPositionState,
    uiSettings = MapUiSettings(
        compassEnabled = false,
        indoorLevelPickerEnabled = false,
        mapToolbarEnabled = false,
        myLocationButtonEnabled = false,
        rotationGesturesEnabled = false,
        scrollGesturesEnabled = false,
        scrollGesturesEnabledDuringRotateOrZoom = false,
        tiltGesturesEnabled = false,
        zoomControlsEnabled = false,
        zoomGesturesEnabled = false,
    ),
    liteMode = true,
    modifier = Modifier
        .height(115.dp)
        .fillMaxWidth(),
) {
    val markerState = remember(coordinates) { MarkerState(position = coordinates) }
    MarkerComposable(
        state = markerState,
        title = stringResource(R.string.some_description),
    ) {
        Icon(
            imageVector = MyDrawables.MapMarker,
            contentDescription = null,
            tint = Color.Blue,
            modifier = Modifier.size(16.dp),
        )
    }
}

I am already defining a size for the icon, so my only idea would be to use requiredSize instead to ignore any incoming constraints. Do you think that would help? Unfortunately this issue is relatively rare and we could not reproduce it yet locally, only ours users are able to get it.