In a few recent projects, I’ve needed to utilise Google Maps within environments utilising Jetpack Compose. In the early days of compose this felt light a sought after piece of functionality – even though it is still being built on, it now seems to be in a place where I can confidently use it. In this series of blog posts, I’ll share how we can use the different parts of the compose mapping package. We’ll start here by getting the GoogleMap composable setup and learning a little bit about it’s high level offering.
Looking to learn more Jetpack Compose? Check out Practical Jetpack Compose 🚀
Before we can get started with using the GoogleMap composable, we’ll need to add the required dependency to our project.
implementation("com.google.maps.android:maps-compose:6.1.0")
Next, we’ll need to an API key to our project. You can follow this guide for obtaining a key, followed by adding it into the manifest file for your application:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="your_api_key" />
With this in place, we’ll be able to access the maps API and load a Google Map instance into our application. For this, we’re going to be utilising the GoogleMap composable. Looking at the source for this, we can see the composable supports a range of properties for customizing the display and behaviour of the google map instance.
@Composable
public fun GoogleMap(
mergeDescendants: Boolean = false,
modifier: Modifier = Modifier,
cameraPositionState: CameraPositionState = rememberCameraPositionState(),
contentDescription: String? = null,
googleMapOptionsFactory: () -> GoogleMapOptions = { GoogleMapOptions() },
properties: MapProperties = DefaultMapProperties,
locationSource: LocationSource? = null,
uiSettings: MapUiSettings = DefaultMapUiSettings,
indoorStateChangeListener: IndoorStateChangeListener = DefaultIndoorStateChangeListener,
onMapClick: ((LatLng) -> Unit)? = null,
onMapLongClick: ((LatLng) -> Unit)? = null,
onMapLoaded: (() -> Unit)? = null,
onMyLocationButtonClick: (() -> Boolean)? = null,
onMyLocationClick: ((Location) -> Unit)? = null,
onPOIClick: ((PointOfInterest) -> Unit)? = null,
contentPadding: PaddingValues = NoPadding,
content: (@Composable @GoogleMapComposable () -> Unit)? = null,
)
One important thing to note here is the scoping of the content argument. This is using the GoogleMapComposable scope, meaning that we can only use composables that are scoped to this inside of the google map content (so we can’t just use any old composable). This restricts the GoogleMap composable quite a lot in terms of extensibility, but it means that there are standards enforced for the composable (for both UX and performance reasons).
So we can see some results in our project, we’ll go ahead and start by composing the GoogleMap. For now we’ll simply pass a modifier to instruct the composable to fill the maximum available size on the screen.
GoogleMap(
modifier = Modifier.fillMaxSize()
)
With very minimal work involved, we can now see the display of a basic GoogleMap composable within our UI.
As this is, there isn’t much going on and it probably is much use for our application in this state. When it comes to customising the look and feel of the map UI, there are three key arguments in the composable – these are the CameraPositionState, MapUiSettings and MapProperties. We won’t dive too much into these right now, but we’ll take a quick look at the use of these classes to familiarise ourselves with the composable.
Often when loading a map we’ll want to highlight a specific area/location and to do this, we’ll need to provide the map with some coordinates to do so. We’ll need to start here by defining a new LatLng reference and providing some coordinates to be used (we’ll use some coordinates in London for this). Next, we’ll need to construct a CameraPosition reference – this defines the target location to be focused on in the center of the map, along with the zoom-level. To build this, we’ll utilise the rememberCameraPositionState composable function which will allow this information to be saved and persisted across recompositions. Within this block, we’ll use the fromLatLngZoom function to create this reference using our defined coordinates along with a zoom-level.
val london = LatLng(51.5072, 0.1276)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(london, 12f)
}
With this now defined, we can provide this to our composable in the form of the cameraPositionState argument.
GoogleMap(
modifier = modifier,
cameraPositionState = cameraPositionState
)
With this in place we can now see the map centered and zoomed to the location that we’ve provided.
Alongside this customisation, we also have the MapUiSettings which we can use to control the allowed behaviours on our map. For example, if we wish to disable any zoom functionality then we can block this via the zoomControlsEnabled and zoomGesturesEnabled properties.
GoogleMap(
modifier = modifier,
uiSettings = MapUiSettings(zoomControlsEnabled = false, zoomGesturesEnabled = false),
cameraPositionState = cameraPositionState
)
With this in place we can see the zoom controls are now hidden from view and if you interact with the map, you’ll notice that the zoom functionality is not available via gestures.
As it is, our map looks a standard implementation of google maps. Some application utilise the styling wizard from Google Maps to build customized styling for map instances – this allow us to control the colors used, along with the level of POI, roads and labels that are displayed on our map. In these cases, we’ll want to be able to provide this styling to our composable. We can do this via the MapProperties class and its mapStyleOptions property. For this, we’ll need to extra the json content from a resource file in our application. and then provide this content to the MapStyleOptions class.
val jsonStyle = context.assets.assetsFile("style.json")
GoogleMap(
modifier = modifier,
properties = MapProperties(mapStyleOptions = MapStyleOptions(jsonStyle))
)
With this in place, we can now see the custom styling applied to our map composable. This might not be something that every application needs to utilise, but it allows us to apply specific styling based on theming or level of information that needs to be displayed on the map.
In this blog post we’ve been able to take a high-level look at the GoogleMap composable, plugging it into our application and exploring some basic customisation through the arguments it supports. In the following posts, we’ll start to look at customising our map further through Markers and other composables that are supported through the GoogleMapComposable content scope.