Maps Compose Library Tutorial for Android: Getting Started
Learn how to use the Maps Compose library for the Maps SDK for Android to add maps to your Android app. By Harun Wangereka.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Maps Compose Library Tutorial for Android: Getting Started
25 mins
- Getting Started
- Setting Up
- Requesting Location Permissions
- Displaying a Position in a Map
- Adding a Marker on the Map
- Showing Map Information Windows
- Drawing Circles on Your Map
- Customizing the Appearance of Your Map
- Creating a Custom JSON Map Styling
- Applying Custom Style to Your Map
- Requesting Location Updates
- Marking Polygon Positions
- Writing Map UI Tests
- Where to Go From Here?
Adding a Marker on the Map
Inside presentation/composables/MapView.kt, add a pair of curly braces to GoogleMap
composable and add the following in the block:
Marker(
state = MarkerState(position = location),
)
Add any missing imports by pressing Option-Return on a Mac or Alt-Enter on a Windows PC. Your final result will be:
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Marker(
state = MarkerState(position = location),
)
}
You add a marker in a map by adding child composables to GoogleMap
as contents. A Marker
requires a MarkerState
instance that observes marker state such as its position and information window.
Pass the Singapore location to MarkerState
and then build and run the app.
You can see the red marker for Singapore at the center of your map.
Often, you’ll need to show information when a user taps a marker. For that, you’ll have to add InfoWindow
to your map, which you’ll learn next.
Showing Map Information Windows
Head back to presentation/composables/MapView.kt and add this code below the cameraPositionState
variable:
val infoWindowState = rememberMarkerState(position = location)
You have now created a state variable for the marker properties and passed the location to this marker.
Next, below your Marker
composable, add:
MarkerInfoWindow(
state = infoWindowState,
title = "My location",
snippet = "Location custom info window",
content = {
CustomInfoWindow(title = it.title, description = it.snippet)
}
)
In the code above, you create your information window using MarkerInfoWindow
composable. You can customize your information window to your liking. You pass the state
, title
, snippet
and content
as parameters. Inside the content lambda, you call your custom composable with your information window custom view.
Build and run the app. Tap the Singapore marker, and you should see:
The information window displays on top of the marker with texts from the title
and snippet
you passed as parameters.
Drawing Circles on Your Map
So far, you’ve seen how to add markers and info windows to your map. In this section, you’ll add another shape, a Circle
.
In MapView.kt, add the following below MarkerInfoWindow
in the GoogleMap
composable:
Circle(
center = location,
fillColor = MaterialTheme.colorScheme.secondaryContainer,
strokeColor = MaterialTheme.colorScheme.secondaryContainer,
radius = 300.00
)
Resolve the MaterialTheme
missing imports by pressing Option-Return on a Mac or Alt-Enter on a PC.
Circle
is yet another map child composable and has several parameters. For this tutorial, you only need to assign values to:
- center – the
LatLng
that represents the center of this circle. - fillColor – fill color of the circle.
- strokeColor – color of the outer circle or stroke.
- radius – circle radius.
Build and run the app.
You can now see a blue circle at the center of your map. Its center is the Singapore location that you passed.
So far, you’ve drawn several shapes on your map. In the next section, you’ll learn how to customize your map’s appearance by adding a custom JSON map style.
Customizing the Appearance of Your Map
There are two map styling options available with maps:
- Cloud-based styling: This allows you to create and edit map styles without requiring any changes in your app. You make all the changes in the cloud console, which are reflected on your apps once you have a map ID.
- JSON based styling: Here, you create a map style on the old style wizard . Once you complete the customization, you can download the JSON file and add it to your map.
In this tutorial, you’ll be using JSON styling. You’ll create your custom style to add to the map in the next section.
Creating a Custom JSON Map Styling
Open your preferred browser and head to the old style wizard. You should see:
On the left, you have customization options such as changing the density of the features and changing the theme of your map.
Start by selecting the Silver theme as shown below:
On the right side, you can see the map color changes to reflect the selected theme. Next, click MORE OPTIONS as shown above.
This shows a list of features you can customize and visualize on the map. For this tutorial, you’ll customize the Road feature.
Follow these steps:
- Click the Road feature, which will open up the element type section on the right.
- The elements type section has a list of elements you can customize, which in this case are labels and geometry.
- Click the Geometry option and change the color as per your preference. You can see the color is immediately reflected on the map.
That’s all for now. You can add as many customization options as you wish. Click FINISH, and you’ll see the Export Style dialog as shown:
Click COPY JSON option. This copies the JSON style in your clipboard. You’re now a few steps away from applying the custom style to your compose map.
Navigate back to Android Studio. Right-click the res directory, choose New ▸ Android Resource Directory and select raw. In the new raw directory, create a file named map_style.json and paste the copied style here.
Now, you have the style ready for use. Next, you need to apply it to your map.
Applying Custom Style to Your Map
Head over to presentation/composables/MapView.kt. Below your infoWindowState
variable add:
val mapProperties by remember {
mutableStateOf(
MapProperties(
mapStyleOptions = MapStyleOptions.loadRawResourceStyle(context, R.raw.map_style)
)
)
}
Add any missing imports by pressing Option-Return on a Mac or Alt-Enter on a PC. As seen above, you create a new state variable of type MapProperties
. This variable holds properties you can change on the map. You pass the custom style to the mapStyleOptions
, which loads the style from the raw directory.
Next, add this variable mapProperties
as properties
parameter to your GoogleMap
. Your final result should be:
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = mapProperties
) {
// Child Composables
}
Build and run the app.
You can see your map now applies the style from your JSON file.
Requesting Location Updates
A common feature of maps on devices is the ability for them to update in real time. To do that here, You’ll use a callbackFlow
to request for location updates. Inside utils package you’ll find LocationUtils.kt file. The location callbackFlow
is as follows:
@SuppressLint("MissingPermission")
fun FusedLocationProviderClient.locationFlow() = callbackFlow {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
try {
trySend(result.lastLocation)
} catch (e: Exception) {
Log.e("Error", e.message.toString())
}
}
}
requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper())
.addOnFailureListener { e ->
close(e)
}
awaitClose {
removeLocationUpdates(callback)
}
}
Here, you wrap your LocationCallback
in a callbackFlow
. In the callbackFlow
, callback
is called whenever you have location updates from requestLocationUpdates
. And finally, you clean up resources when your callback is removed inside awaitClose
.
Open up MainActivity.kt, and check out fetchLocationUpdates()
to see how it fetches location updates:
private fun fetchLocationUpdates() {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
fusedLocationClient.locationFlow().collect {
it?.let { location ->
geoMarkerViewModel.setCurrentLatLng(LatLng(location.latitude, location.longitude))
}
}
}
}
}
This uses repeatOnLifecycle()
to collect safely from your Flow in the UI. You also pass the location to your viewmodel to share the latest value with your composable.
In the next section, you’ll see how to draw polygons on your map and finish the geo marking part of the app.