Integrating Google Maps with Jetpack Compose



Introduction

Jetpack Compose has revolutionized Android app development by providing a modern and intuitive way to build UIs. One exciting aspect of this toolkit is its seamless integration with other Android features, including Google Maps. In this article, we’ll explore how to harness the power of Jetpack Compose to integrate Google Maps into your Android app, opening up a world of possibilities for location-based experiences.

Integrating Google Maps API

google-map = "4.3.0" //make sure to use the latest version
dependencies {
implementation("com.google.maps.android:maps-compose:google-map")
implementation("com.google.maps.android:maps-compose-utils:google-map")
implementation("com.google.maps.android:maps-compose-widgets:google-map")
}

Add the Map Key to the Manifest

First, add the key in the manifest file and then in the gradle file like the following:

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="$MAPS_API_KEY" />

manifestPlaceholders["MAPS_API_KEY"] = "YOUR_KEY_HERE"

Additionally, secure the key by following this article: Ways to secure the key.

Creating the Google Map Composable

We can use the GoogleMap composable function to set up a map composable. This function takes the camera position state as a parameter to built using the CameraPosition.fromLatLngZoom() function.

Make sure to request the required runtime permission to check the functionality: google accompanist permissions.

val lahoreMap = LatLng(31.5204, 74.3587)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(lahoreMap, 12f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
)

Congratulations, the Google Map composable setup is done 🥳 🥳 🥳. Let’s dive into more details. Further, we will explore how we can customize our map’s visibility and behavior by adding custom markers, and polylines and enabling user interactions.

Customizing Map appearance and controls

Composable provides different features to provide the control to the user to interact with the Map with ease. For example, we can enable or disable zoom controls by zoomControlsEnabled, and change the Map appearance by updating mapType and more.

We have 5 different visibility options i.e. NONENORMALTERRAINSATELITEHYBRID


val lahoreMap = LatLng(31.5204, 74.3587)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(lahoreMap, 12f)
}

var uiSettings by remember {
mutableStateOf(MapUiSettings(zoomControlsEnabled = true))
}
var properties by remember {
mutableStateOf(MapProperties(mapType = MapType.SATELLITE))
}

GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = properties,
uiSettings = uiSettings
)

Adding Markers

Enhance the map by adding markers to denote specific locations. We can customize the appearance of these markers to match the app’s design aesthetic and add additional layers of information to the map. Experiment with different marker styles and explore the various customization options available through the Google Maps API.

1- Simple Marker

Here is an example of displaying the simple built-in Markers on the Map:

val latLng = LatLng(20.302039, 134.2082661)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(latLng, 2f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Marker(
state = MarkerState(position = LatLng(-34.0, 151.0)),
title = "Marker in Sydney"
)
Marker(
state = MarkerState(position = LatLng(35.66, 139.6)),
title = "Marker in Tokyo"
)
}

2- Custom Marker

We can add custom markers with different icons by MarkerComposable. Here is an example of a Star Marker displayed over the Map:

val latLng = LatLng(20.302039, 134.2082661)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(latLng, 2f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Marker(
state = MarkerState(position = LatLng(-34.0, 151.0)),
title = "Marker in Sydney"
)
Marker(
state = MarkerState(position = LatLng(35.66, 139.6)),
title = "Marker in Tokyo"
)
MarkerComposable(
state = MarkerState(position = LatLng(11.6600892, 117.3276336)),
) {
Icon(
imageVector = Icons.Filled.Star,
contentDescription = "Star Icon",
tint = Color.Blue,
modifier = Modifier.size(58.dp)
)
}
}

User Interaction Window

User interactions are key to creating a dynamic and engaging map experience. Update the UI dynamically based on these interactions to provide a seamless and intuitive user experience.

Composable provides the MarkerInfoWindow functionality that pops up the window when a marker is clicked. This window can have its own composable, we can customize it to have images, descriptions, and interactive elements, providing a rich user experience.

val latLng = LatLng(20.302039, 134.2082661)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(latLng, 2f)
}
val markerState = MarkerState(LatLng(14.5892056, 120.9646546))

GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
MarkerInfoWindow(
state = markerState,
icon = bitmapDescriptorFromVector(LocalContext.current, R.drawable.location_pin)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.border(
BorderStroke(1.dp, Color.Black),
RoundedCornerShape(24)
)
.clip(RoundedCornerShape(24))
.background(Color.Red)
.padding(20.dp)
) {
Icon(
Icons.Filled.Info,
contentDescription = null,
modifier = Modifier.size(36.dp)
)
Text("Manila", fontWeight = FontWeight.Bold)
Text("Capital of the Philippines", fontWeight = FontWeight.Medium)
}
}
}

Adding Polylines

As we are already familiar with the Polylines that allows users to explore the specified route within the app. This code showcases how to build an interactive route map. We can extend it to the desired routes.

Further, on click we can show the user interaction windows or the desired UI.

val routeCoordinates = listOf(
LatLng(37.7749, -122.4194), // Starting point (e.g., San Francisco)
LatLng(36.7783, -119.4179), // Waypoint 1
LatLng(34.0522, -118.2437), // Waypoint 2 (e.g., Los Angeles)
LatLng(32.7157, -117.1611) // Ending point (e.g., San Diego)
)
val latLng = LatLng(36.7783, -119.4179)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(latLng, 6f)
}
var selectedRoute by remember { mutableStateOf<Route?>(null) }

GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
// Draw the route polyline
Polyline(
points = routeCoordinates,
clickable = true,
color = Color.Blue,
width = 5f,
tag = CaliforniaRoute,
onClick = { polyline ->
// Handle polyline click event
}
)
}

Adding Polygons

Same goes for the Polygons allow the user to explore the specified routes within the app. This adds interactivity to the map and allows users to interact with the polygons on the map. This code showcases how to build an interactive route map. We can extend it to the desired routes.

Further, on click we can show the user interaction windows, or change the colors or the desired UI

val polygonPoints = listOf(
LatLng(37.7749, -122.4194),
LatLng(37.8049, -122.4400),
LatLng(37.7949, -122.4100)
)
val latLng = LatLng(37.7749, -122.4194)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(latLng, 13f)
}

// Create a mutable state to track whether the polygon is selected
var isPolygonSelected by remember { mutableStateOf(false) }
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Polygon(
points = polygonPoints,
clickable = true,
fillColor = if (isPolygonSelected) Color.Red else Color.Green,
strokeColor = Color.Blue,
strokeWidth = 5f,
tag = "San Francisco",
onClick = { polygon ->
// Handle polygon click event
isPolygonSelected = true
}
)
}

Adding Circles

Same goes for Circles which allows users to explore the specified routes within the app. This adds interactivity to the map and allows users to interact on the map. This code showcases how to build an interactive route map. We can extend it to the desired routes.

Further, on click we can show the user interaction windows, or change the colors or the desired UI

val circleData = listOf(
LatLng(37.7749, -122.4194),
LatLng(36.7783, -119.4179),
LatLng(34.0522, -118.2437)
)
val radius by remember {
mutableStateOf(5000.0)
}
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(LatLng(36.7783, -119.4179), 11f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
// Draw clickable circles for each location
circleData.forEach { circleInfo ->
Circle(
center = circleInfo,
clickable = true,
fillColor = Color.Blue.copy(alpha = 0.3f),
radius = radius, // Specify the radius in meters
strokeColor = Color.Black,
strokeWidth = 2f,
tag = circleInfo,
onClick = { circle ->
// Handle circle click event
}
)
}
}

Street View

Street View provides users with immersive experiences, allowing them to explore real-world locations in a 360-degree panoramic view. The StreetView composable displays the panorama, allowing users to explore the location using panning gestures, zoom gestures, and navigation controls.

val constitutionAve = LatLng(38.8921, -77.0067)
StreetView(
streetViewPanoramaOptionsFactory = {
StreetViewPanoramaOptions().position(constitutionAve)
},
isPanningGesturesEnabled = true,
isStreetNamesEnabled = true,
isUserNavigationEnabled = true,
isZoomGesturesEnabled = true
)

Conclusion

By integrating Google Maps with Jetpack Compose, you can create immersive and interactive map experiences that enhance your Android apps. The addition of markers, polylines, polygons, and Street View provides valuable context and information to your users, making your app more engaging and useful. Experiment with the concepts covered in this article to unlock the full potential of maps in your app projects.

If you have any questions or feedback, please feel free to contact me.

Connect with me on LinkedIn.

Best Regards.

Comments

Popular posts from this blog

Ways to Secure Android Secret Keys

The Mobile App Colosseum: Flutter vs. React Native - A Developer Showdown