From 632995427543e1a9bcc632c77e9c3ecf5a27f280 Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Sat, 16 Dec 2023 15:05:21 -0500 Subject: [PATCH 1/7] fix: Use recomposed map listener callbacks rather than only the initially composed version Fixes #466 --- .../google/maps/android/compose/GoogleMap.kt | 10 +- .../google/maps/android/compose/MapApplier.kt | 6 + .../maps/android/compose/MapClickListeners.kt | 128 ++++++++++++++++++ .../google/maps/android/compose/MapUpdater.kt | 22 --- 4 files changed, 140 insertions(+), 26 deletions(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt index 2d292a2a0..8e89cb2a5 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt @@ -36,7 +36,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.semantics.semantics import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver @@ -125,17 +124,19 @@ public fun GoogleMap( val currentContent by rememberUpdatedState(content) LaunchedEffect(Unit) { disposingComposition { - mapView.newComposition(parentComposition) { + mapView.newComposition(parentComposition, mapClickListeners) { MapUpdater( mergeDescendants = mergeDescendants, contentDescription = contentDescription, cameraPositionState = currentCameraPositionState, - clickListeners = mapClickListeners, contentPadding = currentContentPadding, locationSource = currentLocationSource, mapProperties = currentMapProperties, mapUiSettings = currentUiSettings, ) + + MapClickListenerUpdater() + CompositionLocalProvider( LocalCameraPositionState provides cameraPositionState, ) { @@ -157,11 +158,12 @@ internal suspend inline fun disposingComposition(factory: () -> Composition) { private suspend inline fun MapView.newComposition( parent: CompositionContext, + mapClickListeners: MapClickListeners, noinline content: @Composable () -> Unit ): Composition { val map = awaitMap() return Composition( - MapApplier(map, this), parent + MapApplier(map, this, mapClickListeners), parent ).apply { setContent(content) } diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapApplier.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapApplier.kt index 2c9ef6fbf..36e488b0b 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapApplier.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapApplier.kt @@ -31,9 +31,15 @@ internal interface MapNode { private object MapNodeRoot : MapNode +// [mapClickListeners] must be a singleton for the [map] and is therefore stored here: +// [GoogleMap.setOnIndoorStateChangeListener()] will not actually set a new non-null listener if +// called more than once; if [mapClickListeners] were passed through the Compose function hierarchy +// we would need to consider the case of it changing, which would require special treatment +// for that particular listener; yet MapClickListeners never actually changes. internal class MapApplier( val map: GoogleMap, internal val mapView: MapView, + val mapClickListeners: MapClickListeners, ) : AbstractApplier(MapNodeRoot) { private val decorations = mutableListOf() diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt index 15b5a124a..ac3514389 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt @@ -15,9 +15,20 @@ package com.google.maps.android.compose import android.location.Location +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ComposeNode +import androidx.compose.runtime.NonRestartableComposable +import androidx.compose.runtime.currentComposer import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import com.google.android.gms.maps.GoogleMap.OnIndoorStateChangeListener +import com.google.android.gms.maps.GoogleMap.OnMapClickListener +import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback +import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener +import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener +import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener +import com.google.android.gms.maps.GoogleMap.OnPoiClickListener import com.google.android.gms.maps.model.IndoorBuilding import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.PointOfInterest @@ -56,3 +67,120 @@ internal class MapClickListeners { var onMyLocationClick: ((Location) -> Unit)? by mutableStateOf(null) var onPOIClick: ((PointOfInterest) -> Unit)? by mutableStateOf(null) } + +/** + * @param L GoogleMap click listener type, e.g. [OnMapClickListener] + */ +internal class MapClickListenerNode( + private val setter: (L?) -> Unit, + private val listener: L +) : MapNode { + override fun onAttached() = setter(listener) + override fun onRemoved() = setter(null) + override fun onCleared() = setter(null) +} + +@Suppress("ComplexRedundantLet") +@Composable +internal fun MapClickListenerUpdater() { + val mapApplier = currentComposer.applier as MapApplier + + // The mapClickListeners container object is not allowed to ever change + with (mapApplier.mapClickListeners) { + with(mapApplier.map) { + ::indoorStateChangeListener.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnIndoorStateChangeListener, + object : OnIndoorStateChangeListener { + override fun onIndoorBuildingFocused() = + callback().onIndoorBuildingFocused() + + override fun onIndoorLevelActivated(building: IndoorBuilding) = + callback().onIndoorLevelActivated(building) + } + ) + } + + ::onMapClick.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnMapClickListener, + OnMapClickListener { callback()?.invoke(it) } + ) + } + + ::onMapLongClick.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnMapLongClickListener, + OnMapLongClickListener { callback()?.invoke(it) } + ) + } + + ::onMapLoaded.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnMapLoadedCallback, + OnMapLoadedCallback { callback()?.invoke() } + ) + } + + ::onMyLocationButtonClick.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnMyLocationButtonClickListener, + OnMyLocationButtonClickListener { callback()?.invoke() ?: false } + ) + } + + ::onMyLocationClick.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnMyLocationClickListener, + OnMyLocationClickListener { callback()?.invoke(it) } + ) + } + + ::onPOIClick.let { callback -> + MapClickListenerComposeNode( + callback, + ::setOnPoiClickListener, + OnPoiClickListener { callback()?.invoke(it) } + ) + } + } + } +} + +/** + * Encapsulates the ComposeNode factory lambda as a recomposition optimization. + * + * @param L GoogleMap click listener type, e.g. [OnMapClickListener] + * @param listener must include a call to [callback()] inside the listener + * to use the most up-to-date recomposed version of the callback lambda; + * However, the resulting callback reference might actually be null due to races; + * the caller must guard against this case. + * + */ +@Composable +@NonRestartableComposable +private fun MapClickListenerComposeNode( + callback: () -> Any?, + setter: (L?) -> Unit, + listener: L +) = MapClickListenerComposeNode(callback) { MapClickListenerNode(setter, listener) } + +@Composable +private fun MapClickListenerComposeNode( + callback: () -> Any?, + factory: () -> MapClickListenerNode<*> +) { + // Setting a GoogleMap listener may have side effects, so we unset it as needed. + // However, the listener is reset only when the corresponding callback lambda + // toggles between null and non-null. This is to avoid potential performance problems + // when callbacks recompose rapidly; setting GoogleMap listeners could potentially be + // expensive due to synchronization, etc. GoogleMap listeners are not designed with a + // use case of rapid recomposition in mind. + if (callback() != null) ComposeNode, MapApplier>(factory) {} +} diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt index f7ea02570..f27a000d3 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt @@ -26,13 +26,11 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.LocationSource -import com.google.android.gms.maps.model.IndoorBuilding internal class MapPropertiesNode( val map: GoogleMap, cameraPositionState: CameraPositionState, contentDescription: String?, - var clickListeners: MapClickListeners, var density: Density, var layoutDirection: LayoutDirection, ) : MapNode { @@ -76,23 +74,6 @@ internal class MapPropertiesNode( map.setOnCameraMoveListener { cameraPositionState.rawPosition = map.cameraPosition } - - map.setOnMapClickListener(clickListeners.onMapClick) - map.setOnMapLongClickListener(clickListeners.onMapLongClick) - map.setOnMapLoadedCallback(clickListeners.onMapLoaded) - map.setOnMyLocationButtonClickListener { clickListeners.onMyLocationButtonClick?.invoke() == true } - map.setOnMyLocationClickListener(clickListeners.onMyLocationClick) - map.setOnPoiClickListener(clickListeners.onPOIClick) - - map.setOnIndoorStateChangeListener(object : GoogleMap.OnIndoorStateChangeListener { - override fun onIndoorBuildingFocused() { - clickListeners.indoorStateChangeListener.onIndoorBuildingFocused() - } - - override fun onIndoorLevelActivated(building: IndoorBuilding) { - clickListeners.indoorStateChangeListener.onIndoorLevelActivated(building) - } - }) } override fun onRemoved() { @@ -116,7 +97,6 @@ internal inline fun MapUpdater( mergeDescendants: Boolean = false, contentDescription: String?, cameraPositionState: CameraPositionState, - clickListeners: MapClickListeners, contentPadding: PaddingValues = NoPadding, locationSource: LocationSource?, mapProperties: MapProperties, @@ -135,7 +115,6 @@ internal inline fun MapUpdater( map = map, contentDescription = contentDescription, cameraPositionState = cameraPositionState, - clickListeners = clickListeners, density = density, layoutDirection = layoutDirection, ) @@ -181,6 +160,5 @@ internal inline fun MapUpdater( set(mapUiSettings.zoomGesturesEnabled) { map.uiSettings.isZoomGesturesEnabled = it } update(cameraPositionState) { this.cameraPositionState = it } - update(clickListeners) { this.clickListeners = it } } } From eb64e29d9286baa77dbca1939a2a7bf39229a413 Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Sat, 30 Dec 2023 07:55:16 -0500 Subject: [PATCH 2/7] Minor formatting fix --- .../java/com/google/maps/android/compose/MapClickListeners.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt index ac3514389..fe9112aab 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt @@ -86,7 +86,7 @@ internal fun MapClickListenerUpdater() { val mapApplier = currentComposer.applier as MapApplier // The mapClickListeners container object is not allowed to ever change - with (mapApplier.mapClickListeners) { + with(mapApplier.mapClickListeners) { with(mapApplier.map) { ::indoorStateChangeListener.let { callback -> MapClickListenerComposeNode( From 36d8208d77003f65fb01157398efc67b50c667d9 Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Sat, 30 Dec 2023 09:01:24 -0500 Subject: [PATCH 3/7] Label leaf composable as @GoogleMapComposable for proper compile-time diagnostics --- .../java/com/google/maps/android/compose/MapClickListeners.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt index fe9112aab..7411c497e 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt @@ -172,6 +172,7 @@ private fun MapClickListenerComposeNode( ) = MapClickListenerComposeNode(callback) { MapClickListenerNode(setter, listener) } @Composable +@GoogleMapComposable private fun MapClickListenerComposeNode( callback: () -> Any?, factory: () -> MapClickListenerNode<*> From 5bc5aca22aeb15d544f7d3b2b0b0cf8f54da482a Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Sun, 31 Dec 2023 12:31:31 -0500 Subject: [PATCH 4/7] Clarify documentation --- .../com/google/maps/android/compose/MapClickListeners.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt index 7411c497e..1b8342644 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt @@ -157,7 +157,10 @@ internal fun MapClickListenerUpdater() { * Encapsulates the ComposeNode factory lambda as a recomposition optimization. * * @param L GoogleMap click listener type, e.g. [OnMapClickListener] - * @param listener must include a call to [callback()] inside the listener + * @param callback a property reference to the callback lambda, i.e. + * invoking it returns the callback lambda + * @param setter a reference to a GoogleMap setter method, e.g. `setOnMapClickListener()` + * @param listener must include a call to `callback()` inside the listener * to use the most up-to-date recomposed version of the callback lambda; * However, the resulting callback reference might actually be null due to races; * the caller must guard against this case. From dff2b0a13a7c0c4939ce310be8131fc653737492 Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Tue, 2 Jan 2024 19:11:41 -0500 Subject: [PATCH 5/7] Address spurious subcomposition recompositions by delaying state updates to after parent composition, not during parent composition --- .../google/maps/android/compose/GoogleMap.kt | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt index 8e89cb2a5..90dfaab02 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -105,15 +106,20 @@ public fun GoogleMap( // rememberUpdatedState and friends are used here to make these values observable to // the subcomposition without providing a new content function each recomposition - val mapClickListeners = remember { MapClickListeners() }.also { - it.indoorStateChangeListener = indoorStateChangeListener - it.onMapClick = onMapClick - it.onMapLongClick = onMapLongClick - it.onMapLoaded = onMapLoaded - it.onMyLocationButtonClick = onMyLocationButtonClick - it.onMyLocationClick = onMyLocationClick - it.onPOIClick = onPOIClick + val mapClickListeners = remember { MapClickListeners() } + + SideEffect { + mapClickListeners.also { + it.indoorStateChangeListener = indoorStateChangeListener + it.onMapClick = onMapClick + it.onMapLongClick = onMapLongClick + it.onMapLoaded = onMapLoaded + it.onMyLocationButtonClick = onMyLocationButtonClick + it.onMyLocationClick = onMyLocationClick + it.onPOIClick = onPOIClick + } } + val currentLocationSource by rememberUpdatedState(locationSource) val currentCameraPositionState by rememberUpdatedState(cameraPositionState) val currentContentPadding by rememberUpdatedState(contentPadding) From 001a8eadf5c793ca87398fbd1e668f783510b72a Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Fri, 5 Jan 2024 15:56:17 -0500 Subject: [PATCH 6/7] Delay GoogleMap object access until composition apply phase (see #501) --- .../maps/android/compose/MapClickListeners.kt | 148 +++++++++--------- 1 file changed, 77 insertions(+), 71 deletions(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt index 1b8342644..42b3ee6e6 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.currentComposer import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.GoogleMap.OnIndoorStateChangeListener import com.google.android.gms.maps.GoogleMap.OnMapClickListener import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback @@ -72,83 +73,84 @@ internal class MapClickListeners { * @param L GoogleMap click listener type, e.g. [OnMapClickListener] */ internal class MapClickListenerNode( - private val setter: (L?) -> Unit, + private val map: GoogleMap, + private val setter: GoogleMap.(L?) -> Unit, private val listener: L ) : MapNode { - override fun onAttached() = setter(listener) - override fun onRemoved() = setter(null) - override fun onCleared() = setter(null) + override fun onAttached() = setListener(listener) + override fun onRemoved() = setListener(null) + override fun onCleared() = setListener(null) + + private fun setListener(listenerOrNull: L?) = map.setter(listenerOrNull) } @Suppress("ComplexRedundantLet") @Composable internal fun MapClickListenerUpdater() { - val mapApplier = currentComposer.applier as MapApplier - // The mapClickListeners container object is not allowed to ever change - with(mapApplier.mapClickListeners) { - with(mapApplier.map) { - ::indoorStateChangeListener.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnIndoorStateChangeListener, - object : OnIndoorStateChangeListener { - override fun onIndoorBuildingFocused() = - callback().onIndoorBuildingFocused() - - override fun onIndoorLevelActivated(building: IndoorBuilding) = - callback().onIndoorLevelActivated(building) - } - ) - } - - ::onMapClick.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnMapClickListener, - OnMapClickListener { callback()?.invoke(it) } - ) - } - - ::onMapLongClick.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnMapLongClickListener, - OnMapLongClickListener { callback()?.invoke(it) } - ) - } - - ::onMapLoaded.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnMapLoadedCallback, - OnMapLoadedCallback { callback()?.invoke() } - ) - } - - ::onMyLocationButtonClick.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnMyLocationButtonClickListener, - OnMyLocationButtonClickListener { callback()?.invoke() ?: false } - ) - } - - ::onMyLocationClick.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnMyLocationClickListener, - OnMyLocationClickListener { callback()?.invoke(it) } - ) - } - - ::onPOIClick.let { callback -> - MapClickListenerComposeNode( - callback, - ::setOnPoiClickListener, - OnPoiClickListener { callback()?.invoke(it) } - ) - } + val mapClickListeners = (currentComposer.applier as MapApplier).mapClickListeners + + with(mapClickListeners) { + ::indoorStateChangeListener.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnIndoorStateChangeListener, + object : OnIndoorStateChangeListener { + override fun onIndoorBuildingFocused() = + callback().onIndoorBuildingFocused() + + override fun onIndoorLevelActivated(building: IndoorBuilding) = + callback().onIndoorLevelActivated(building) + } + ) + } + + ::onMapClick.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnMapClickListener, + OnMapClickListener { callback()?.invoke(it) } + ) + } + + ::onMapLongClick.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnMapLongClickListener, + OnMapLongClickListener { callback()?.invoke(it) } + ) + } + + ::onMapLoaded.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnMapLoadedCallback, + OnMapLoadedCallback { callback()?.invoke() } + ) + } + + ::onMyLocationButtonClick.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnMyLocationButtonClickListener, + OnMyLocationButtonClickListener { callback()?.invoke() ?: false } + ) + } + + ::onMyLocationClick.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnMyLocationClickListener, + OnMyLocationClickListener { callback()?.invoke(it) } + ) + } + + ::onPOIClick.let { callback -> + MapClickListenerComposeNode( + callback, + GoogleMap::setOnPoiClickListener, + OnPoiClickListener { callback()?.invoke(it) } + ) } } } @@ -170,9 +172,13 @@ internal fun MapClickListenerUpdater() { @NonRestartableComposable private fun MapClickListenerComposeNode( callback: () -> Any?, - setter: (L?) -> Unit, + setter: GoogleMap.(L?) -> Unit, listener: L -) = MapClickListenerComposeNode(callback) { MapClickListenerNode(setter, listener) } +) { + val mapApplier = currentComposer.applier as MapApplier + + MapClickListenerComposeNode(callback) { MapClickListenerNode(mapApplier.map, setter, listener) } +} @Composable @GoogleMapComposable From c642315ed25aa036eb849f9ff0a9c6cb9acec915 Mon Sep 17 00:00:00 2001 From: Uli Bubenheimer Date: Wed, 24 Jan 2024 11:59:03 -0500 Subject: [PATCH 7/7] Revert "Address spurious subcomposition recompositions by delaying state updates to after parent composition, not during parent composition" This reverts commit dff2b0a13a7c0c4939ce310be8131fc653737492. --- .../google/maps/android/compose/GoogleMap.kt | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt index 90dfaab02..8e89cb2a5 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState -import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -106,20 +105,15 @@ public fun GoogleMap( // rememberUpdatedState and friends are used here to make these values observable to // the subcomposition without providing a new content function each recomposition - val mapClickListeners = remember { MapClickListeners() } - - SideEffect { - mapClickListeners.also { - it.indoorStateChangeListener = indoorStateChangeListener - it.onMapClick = onMapClick - it.onMapLongClick = onMapLongClick - it.onMapLoaded = onMapLoaded - it.onMyLocationButtonClick = onMyLocationButtonClick - it.onMyLocationClick = onMyLocationClick - it.onPOIClick = onPOIClick - } + val mapClickListeners = remember { MapClickListeners() }.also { + it.indoorStateChangeListener = indoorStateChangeListener + it.onMapClick = onMapClick + it.onMapLongClick = onMapLongClick + it.onMapLoaded = onMapLoaded + it.onMyLocationButtonClick = onMyLocationButtonClick + it.onMyLocationClick = onMyLocationClick + it.onPOIClick = onPOIClick } - val currentLocationSource by rememberUpdatedState(locationSource) val currentCameraPositionState by rememberUpdatedState(cameraPositionState) val currentContentPadding by rememberUpdatedState(contentPadding)