Fabio Berta
04/11/2023, 2:22 PMAndroidView
. I'm using it to wrap a maplibre (mapbox v9 fork) map in a composable Map
. If I store the current camera position of the map as a mutableStateOf
in Map
, add a position listener that updates the state in the factory
and set the position
in the update
block of the AndroidView
, all is fine: the position updates when the state changes and the AndroidView
itself does not recompose. But if that state is hoisted outside of Map
and passed in as a param, AndroidView
itself recomposes and the performance is bad. I checked that the factory
is only called once though and update
is called correctly. I'm trying to figure out 2 things, why does AndroidView
itself recompose in this case and not just call update
and why is the performance bad, it seems like factory
is not called again but only updated
which seems like what is supposed to happen.Jakub Syty
04/11/2023, 2:58 PMJakub Syty
04/11/2023, 2:58 PMephemient
04/11/2023, 3:03 PMFabio Berta
04/11/2023, 3:04 PMonReset
but thought this was more appropriate for lazy layoutsZach Klippenstein (he/him) [MOD]
04/11/2023, 8:56 PMFabio Berta
04/11/2023, 9:43 PMFabio Berta
04/11/2023, 9:43 PM@Composable
fun HoistedStateMap(
position: MapPosition,
onPositionChange: (MapPosition) -> Unit,
modifier: Modifier = Modifier,
) {
AndroidView(
factory = { context ->
MapView(context).also { mapView ->
mapView.getMapAsync { map ->
map.apply {
addOnCameraMoveListener { onPositionChange(cameraPosition.toMapPosition()) }
setStyle(STYLE_URL)
}
}
}
},
update = { mapView ->
mapView.getMapAsync { map ->
position.toCameraPosition().let {
if (it != map.cameraPosition) {
map.cameraPosition = it
}
}
}
},
modifier = modifier,
)
}
Fabio Berta
04/11/2023, 9:43 PMFabio Berta
04/11/2023, 9:43 PM@Composable
fun InternalStateMap(
modifier: Modifier = Modifier,
) {
var position by remember { mutableStateOf(initialPosition) }
AndroidView(
factory = { context ->
MapView(context).also { mapView ->
mapView.getMapAsync { map ->
map.apply {
addOnCameraMoveListener { position = cameraPosition.toMapPosition() }
setStyle(STYLE_URL)
}
}
}
},
update = { mapView ->
mapView.getMapAsync { map ->
position.toCameraPosition().let {
if (it != map.cameraPosition) {
map.cameraPosition = it
}
}
}
},
modifier = modifier,
)
}
Fabio Berta
04/11/2023, 9:44 PMFabio Berta
04/11/2023, 9:47 PMFabio Berta
04/11/2023, 9:47 PMdata class MapPosition(val coordinates: Coordinates, val zoom: Double)
ephemient
04/11/2023, 9:47 PMMapPosition
@Stable
or @Immutable
or neither?Fabio Berta
04/11/2023, 9:48 PMdata class Coordinates(val lat: Latitude, val lon: Longitude)
@JvmInline
value class Latitude(val value: Double)
@JvmInline
value class Longitude(val value: Double)
Fabio Berta
04/11/2023, 9:48 PMFabio Berta
04/11/2023, 9:50 PMMapPosition
with Stable doesn't change anythingFabio Berta
04/11/2023, 9:55 PMFabio Berta
04/14/2023, 3:26 PMMapPosition
was indeed not stable because it came from a module without compose. I adjusted that and now it is stable and the composable is skippable. However, nothing changed in terms of performance. Obviously, HoistedStateMap
can't be skipped because position
actually changes. In that sense, it also makes sense that this change didn't have any effect.Fabio Berta
04/14/2023, 3:26 PMFabio Berta
04/14/2023, 3:26 PM@Composable
fun HoistedStateMap(
position: MapPosition,
onPositionChange: (MapPosition) -> Unit,
modifier: Modifier = Modifier,
) {
val mapPosition = rememberUpdatedState(position)
AndroidView(
factory = { context ->
MapView(context).also { mapView ->
mapView.getMapAsync { map ->
map.apply {
addOnCameraMoveListener { onPositionChange(cameraPosition.toMapPosition()) }
setStyle(STYLE_URL)
}
}
}
},
update = { mapView ->
mapView.getMapAsync { map ->
mapPosition.value.toCameraPosition().let {
if (it != map.cameraPosition) {
map.cameraPosition = it
}
}
}
},
modifier = modifier,
)
}
Fabio Berta
04/14/2023, 3:26 PMmapPosition
from rememberUpdatedState(position)
is an object that itself doesn't changeFabio Berta
04/14/2023, 3:27 PMupdate
function of AndroidView
is supposed to be used if this is required?Fabio Berta
04/14/2023, 3:27 PMZach Klippenstein (he/him) [MOD]
04/14/2023, 4:23 PMFabio Berta
04/14/2023, 4:23 PMFabio Berta
04/14/2023, 4:24 PMZach Klippenstein (he/him) [MOD]
04/14/2023, 4:28 PMFabio Berta
04/14/2023, 4:30 PM