9

Using Google Maps in a Jetpack Compose app - Part 2!

 2 years ago
source link: https://johnoreilly.dev/posts/jetpack-compose-google-maps-part2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Using Google Maps in a Jetpack Compose app - Part 2!

04 Feb 2022

Share on:

In a previous article back in July of 2020 I outlined how Google Maps support could be added to a Jetpack Compose app based on what was available at that time (using AndroidView). I mentioned in that article that it “seems like this is something that ultimately will exist as specific Jetpack Compose @Composable” and was very happy to see following tweet recently about availability of new Maps Compose component. In this post I’m going to describe how the Galway Bus Kotlin Multiplatform app was updated to use this component.

The dream is now a reality 🤩 I'm excited to share Maps Compose, a library to help you add a Google Map in Jetpack Compose. Go check it out: https://t.co/WyCQcxwXjG #JetpackCompose #AndroidDev https://t.co/0jR1JQiYfd

— Chris Arriola (@arriolachris) February 3, 2022

Maps Compose

Firstly, as outlined in the Maps Compose github page, the following dependencies need to be added to the app’s build.gradle.kts file (based on current versions at ths time).

implementation("com.google.maps.android:maps-compose:1.0.0")
implementation("com.google.android.gms:play-services-maps:18.0.2")

As shown in the screenshot below the app shows the bus stops near to a particular location, both as markers on the map and also in a list as shown below.

galway bus app screenshot

We need to be able to drive the map’s camera position from location, a StateFlow in the view model (the updating of which also in turn triggers querying for list of bus stops close to that location and updating associated state). This can be set in a number of ways:

  • from the device’s location on startup
  • when the user presses the “home” button in the app bar (centering in particular location in the city)
  • as the user pans/changes location on the map

We can manage the setting and observing of changes to that camera position using the component’s CameraPositionState.

val currentLocation by viewModel.location.collectAsState()

val cameraPositionState = rememberCameraPositionState {
    position = CameraPosition.fromLatLngZoom(LatLng(currentLocation.latitude, currentLocation.longitude), 15f)
}

Now we need to manage the interdependent relationship between the location state in the view model and the map’s camera position. Changing one updates the other and vice versa. The way we can do this is using Compose’s snapshotFlow as shown below. Note that I had initially modelled that first dependency using derivedStateOf but ran in to some snapshot related state issues when used with the subsequent snapshotFlow (hope to get chance to dig a bit deeper in to that and figure out why).

snapshotFlow { currentLocation }
    .collect {
        cameraPositionState.position = CameraPosition.fromLatLngZoom(LatLng(currentLocation.latitude, currentLocation.longitude), 15f)
    }

snapshotFlow { cameraPositionState.position }
    .collect {
        viewModel.setLocation(Location(it.target.latitude, it.target.longitude))
    }

Lastly this is how we setup GoogleMap, passing in the cameraPositionState from above and also other initialisation properties along with adding Markers for each of the bus stops.

val mapProperties by remember { mutableStateOf(MapProperties(isMyLocationEnabled = true)) }
val uiSettings by remember { mutableStateOf(MapUiSettings(myLocationButtonEnabled = true)) }

GoogleMap(
    modifier = modifier,
    cameraPositionState = cameraPositionState,
    properties = mapProperties,
    uiSettings = uiSettings
) {
    stops.forEach { stop ->
        val busStopLocation = LatLng(stop.latitude, stop.longitude)
        val icon = bitmapDescriptorFromVector(context, R.drawable.ic_stop, R.color.mapMarkerGreen)
        Marker(position = busStopLocation, title = stop.shortName, icon = icon)
    }
}

A different map in this application shows the live position of buses for a particular route. This uses GoogleMap as above but also needs to set bounds of map based on the positions of those buses. This can be done using something like the following.

val builder = LatLngBounds.Builder()
busInfoList.forEach { bus ->
    val busLocation = LatLng(bus.latitude, bus.longitude)
    builder.include(busLocation)
}

cameraPositionState.move(CameraUpdateFactory.newLatLngBounds(builder.build(), 64))

galway bus app screenshot

The above changes have been pushed to the GalwayBus repo and new version published to Play Store. Note also that Maps Compose’s github repo also includes a sample app that demonstrates other capabilties provided by the library.

Featured in Kotlin Weekly Issue #288

Related tweet

Using Google Maps in a Jetpack Compose app - Part 2! https://t.co/afCUQZ0pbA

Wrote a short follow up article on initial exploration of using the new "Maps Compose" library to add Google Maps support to a #JetpackCompose app. https://t.co/ja2o9a3vsH pic.twitter.com/sDH3V8fyRO

— John O'Reilly (@joreilly) February 5, 2022


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK