Search code examples
androidgoogle-maps-android-api-2android-jetpack-compose

Navigating with Compose not working with Google Maps on Android


I have a composable containing a google maps view. When I click on a pin on the map I would like to trigger navController.navigate so I can navigate to another composable. However, when I call it the application gets stuck instead of navigating. Navigating on a button clicks works as expected.

I have also created a very simple application that is demonstrating the problem. The MainActivity looks like this:

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()
            NavHost(navController = navController, startDestination = "Screen1") {
                composable("Screen1") {
                    Button({ navController.navigate("MapsScreen") }) {
                        Text(text = "Go to Maps")
                    }
                }
                composable("MapsScreen") {
                    val mapView = rememberMapViewWithLifecycle()
                    AndroidView({ mapView }) { mapView ->
                        mapView.getMapAsync { map ->
                            map.setOnInfoWindowClickListener {
                                navController.navigate("Screen1")
                            }

                            val markerOptions = MarkerOptions()
                                .position(LatLng(41.390205, 2.154007))
                                .title("Barcelona")
                            map.addMarker(markerOptions)!!
                        }
                    }
                }
            }
        }
    }
}
@Composable
fun rememberMapViewWithLifecycle(): MapView {
    val context = LocalContext.current
    val mapView = remember {
        MapView(context).apply {
            id = R.id.map
        }
    }

    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(lifecycle, mapView) {
        // Make MapView follow the current lifecycle
        val lifecycleObserver = getMapLifecycleObserver(mapView)
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
        }
    }

    return mapView
}

private fun getMapLifecycleObserver(mapView: MapView): LifecycleEventObserver =
    LifecycleEventObserver { _, event ->
        when (event) {
            Lifecycle.Event.ON_CREATE -> mapView.onCreate(Bundle())
            Lifecycle.Event.ON_START -> mapView.onStart()
            Lifecycle.Event.ON_RESUME -> mapView.onResume()
            Lifecycle.Event.ON_PAUSE -> mapView.onPause()
            Lifecycle.Event.ON_STOP -> mapView.onStop()
            Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
            else -> throw IllegalStateException()
        }
    }

In the project I use androidx.navigation:navigation-compose:2.4.0-beta02 which is the latest version at the moment according to the documentation.

The complete project is available on Github

As an inspiration for the example I have used: Using Google Maps in a Jetpack Compose app and Crane Sample

What could be the problem and how to solve it?


Solution

  • It seems like when you navigate inside the setOnInfoWindowClickListener method, you'll cause an deadlock.

    Try to asynchronously navigate to your target to circumvent the deadlock, e.g. by using GlobalScope.launch on Dispatchers.Main, so the listener can run to the end without any deadlock:

    map.setOnInfoWindowClickListener {
       GlobalScope.launch(Dispatchers.Main) {
          navController.navigate("Screen1")
       }
    }