Hello, I am working on a application utilizing `An...
# compose
a
Hello, I am working on a application utilizing
AndroidView
to display a
MapView
in Compose. I am able to get the map to display fine, but there are interactions within my screen that require me to modify the map after a user clicks a button. i.e. displaying a route on the map after doing a network call from the Google Directions API. The trouble I am having is my
AndroidView
Composable
update
block is not firing when my Directions API call is ready. e.g. when my
mutableStateOf(directions)
changes.
Ill share the implementation for my
AndroidView( MapView )
:
Copy code
@SuppressLint("MissingPermission")
@Composable
fun StationDetailMap(
    mapView: MapView,
    stationViewModel: StationViewModel,
    latitude: String?,
    longitude: String?,
    stationName: String?
) {
    val coroutineScope = rememberCoroutineScope()
    val userLocationState = stationViewModel.userLocation
    val directions = stationViewModel.directions

    AndroidView({
        mapView
    }) { map ->

        coroutineScope.launch {
            val googleMap = map.awaitMap()
            googleMap.apply {

                val latLng = LatLng(latitude!!.toDouble(), longitude!!.toDouble())
                addMarker {
                    position(latLng)
                    title(stationName)
                }

                val boundsBuilder = LatLngBounds.builder()
                boundsBuilder.include(latLng)

                userLocationState.value?.let {
                    boundsBuilder.include(LatLng(it.latitude, it.longitude))
                    isMyLocationEnabled = true
                }

                val polylineOptions = PolylineOptions()
                directions.value?.let {
                    it.routes.firstOrNull()?.overViewPolyline?.polylinePoints?.forEach {
                        polylineOptions.add(LatLng(it.lat, it.lng))
                    }
                    addPolyline(polylineOptions)
                }

                moveCamera(CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(), 100))
            }
        }
    }
}
j
For starters, please read this thread: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1611431183138100 The first lambda parameter to
AndroidView
should be creating the view, not referencing it. The second lambda parameter is not composable, so it will not subscribe to updates. You will need to pull out / build up any relevant information above the invocation of
AndroidView
and the only job of that lambda is to apply the changes to the underlying Android MapView.
To be clear, the fact you're usage is slightly wrong isn't entirely your fault, it's our fault for providing an API that is so susceptible to incorrect usage. We were so happy with what we thought was a beautiful API design, but it doesn't appear to have survived first encounter with the user as well as I would have liked.
a
Ah I see, apologies... I was simply following the "Crane" example from the compose-samples. Specifically these lines: https://github.com/android/compose-samples/blob/main/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt#L139-L175
j
😩
😭
😭 7
ok, it doesn't help when our devrel samples are wrong too 😭
a
Haha thats android dev life man... No worries.
Could you help me understand why the sample went about it this way? The code comment in the lines i shared say:
Copy code
// The MapView lifecycle is handled by this composable. As the MapView also needs to be updated
    // with input from Compose UI, those updates are encapsulated into the MapViewContainer
    // composable. In this way, when an update to the MapView happens, this composable won't
    // recompose and the MapView won't need to be recreated.
which sounds like the intended effect?
c
I do not envy being on dev rel. so many facets of that job. It has been annoying to hit so many issues in the samples though. wish they were more thoroughly vetted iguess. but its alpha?
j
I honestly do not know what they are doing there, the only thing I can think of is that they were updating a sample which was originally written with a very old version of the API and when they upgraded it they just applied a mechanical transform rather than rethinking that code. That, or I'm completely missing something, and there is a subtly/issue that they're trying to work around.
a
I think the main idea is to not have to redraw the entire mapview every time a recompose happens.
i.e.
Copy code
val googleMap = map.awaitMap()
j
But the first lambda parameter will already ensure that doesn't happen. Or at least is supposed to
a
I see
j
a
Hmm perhaps. Odd that its not referenced though.
h
https://github.com/halilozercan/oncallpharmacy/blob/main/androidApp/src/main/java/com/halilibo/eczane/ui/common/ComposeMapView.kt this code for example used to work just fine in alpha09 and it started acting really weird with alpha10. Whenever a slight state change occurs in the parent composable of
ComposeMapView
, map is re-drawn and re-initialized.
I wanted to file an issue but couldn't reproduce it in a smaller setup. It's very hard to track down the issue in this repository, so I just gave up 😕
a
thanks for sharing, this repo looks helpful!
👍 1