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.

5 (5) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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.

Singapore marker

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:

Singapore marker information window

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.

Singapore map with circle

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:

  1. 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.
  2. 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:

JSON map styling wizard

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:

Wizard with silver-theme styling

On the right side, you can see the map color changes to reflect the selected theme. Next, click MORE OPTIONS as shown above.

Styling wizard with more customization options

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.

Styling wizard advanced customization

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:

Styling wizard export style

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 NewAndroid 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.

Map with custom JSON style

You can see your map now applies the style from your JSON file.

Requesting Location Updates

Note: This section is optional. You can skip ahead to Marking Polygon Positions if you want to start adding your geo marking functionality. However, if you’d like to understand how to do location updates, you’re in the right place! The functionality is already in the starter project.

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.