- Notifications
You must be signed in to change notification settings - Fork170
Jetpack Compose composables for the Maps SDK for Android
License
googlemaps/android-maps-compose
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This repository containsJetpack Compose components for theMaps SDK for Android.
- Android API level 21+
- Kotlin-enabled project
- Jetpack Compose-enabled project (seereleases for the required version of Jetpack Compose)
- Sign up with Google Maps Platform
- A Google Maps Platformproject with theMaps SDK for Android enabled
- AnAPI key associated with the project above ... follow theAPI key instructions if you're new to the process
You no longer need to specify the Maps SDK for Android or its Utility Library as separate dependencies, sincemaps-compose andmaps-compose-utils pull in the appropriate versions of these respectively.
dependencies { implementation'com.google.maps.android:maps-compose:6.12.2'// Optionally, you can include the Compose utils library for Clustering,// Street View metadata checks, etc. implementation'com.google.maps.android:maps-compose-utils:6.12.2'// Optionally, you can include the widgets library for ScaleBar, etc. implementation'com.google.maps.android:maps-compose-widgets:6.12.2'}This repository includes asample app.
To run the demo app, ensure you've met the requirements above then:
- Open the
secrets.propertiesfile in your top-level directory, and then add the following code. Replace YOUR_API_KEY with your API key. Store your key in this file because secrets.properties is excluded from being checked into a version control system.If thesecrets.propertiesfile does not exist, create it in the same folder as thelocal.default.propertiesfile.MAPS_API_KEY=YOUR_API_KEY - Build and run
See thedocumentation for a full list of classes and their methods.
Adding a map to your app looks like the following:
val singapore=LatLng(1.35,103.87)val cameraPositionState= rememberCameraPositionState { position=CameraPosition.fromLatLngZoom(singapore,10f)}GoogleMap( modifier=Modifier.fillMaxSize(), cameraPositionState= cameraPositionState)
Creating and configuring a map
Configuring the map can be done by passing aMapProperties object into theGoogleMap composable, or for UI-related configurations, useMapUiSettings.MapProperties andMapUiSettings should be your first go-to for configuringthe map. For any other configuration not present in those two classes, usegoogleMapOptionsFactory to provide aGoogleMapOptions instance instead.Typically, anything that can only be provided once (i.e. when the map iscreated)—like map ID—should be provided viagoogleMapOptionsFactory.
// Set properties using MapProperties which you can use to recompose the mapvar mapProperties by remember { mutableStateOf(MapProperties(maxZoomPreference=10f, minZoomPreference=5f) )}var mapUiSettings by remember { mutableStateOf(MapUiSettings(mapToolbarEnabled=false) )}Box(Modifier.fillMaxSize()) {GoogleMap(properties= mapProperties, uiSettings= mapUiSettings)Column {Button(onClick= { mapProperties= mapProperties.copy( isBuildingEnabled=!mapProperties.isBuildingEnabled ) }) {Text(text="Toggle isBuildingEnabled") }Button(onClick= { mapUiSettings= mapUiSettings.copy( mapToolbarEnabled=!mapUiSettings.mapToolbarEnabled ) }) {Text(text="Toggle mapToolbarEnabled") } }}// ...or initialize the map by providing a googleMapOptionsFactory// This should only be used for values that do not recompose the map such as// map ID.GoogleMap( googleMapOptionsFactory= {GoogleMapOptions().mapId("MyMapId") })
Controlling a map's camera
Camera changes and updates can be observed and controlled viaCameraPositionState.
Note:CameraPositionState is the source of truth for anything camerarelated. So, providing a camera position inGoogleMapOptions will beoverridden byCameraPosition.
val singapore=LatLng(1.35,103.87)val cameraPositionState:CameraPositionState= rememberCameraPositionState { position=CameraPosition.fromLatLngZoom(singapore,11f)}Box(Modifier.fillMaxSize()) {GoogleMap(cameraPositionState= cameraPositionState)Button(onClick= {// Move the camera to a new zoom level cameraPositionState.move(CameraUpdateFactory.zoomIn()) }) {Text(text="Zoom In") }}
Remember that the map must load before any camera state can be set. If you are using a LaunchedEffect, you must wait until the map has been loaded:
@ComposablefunMapScreen() {var mapLoaded by remember { mutableStateOf(false) }GoogleMap( modifier=Modifier.fillMaxSize(), onMapLoaded= { mapLoaded=true } )if (mapLoaded) {LaunchedEffect(Unit) {// here the camera operations } }}
Drawing on a map
Drawing on the map, such as adding markers, can be accomplished by adding childcomposable elements to the content of theGoogleMap.
GoogleMap( googleMapOptionsFactory= {GoogleMapOptions().mapId("DEMO_MAP_ID") },//...) {AdvancedMarker( state=MarkerState(position=LatLng(-34,151)), title="Marker in Sydney" )AdvancedMarker( state=MarkerState(position=LatLng(35.66,139.6)), title="Marker in Tokyo" )}
You can customize a marker by usingPinConfig with anAdvancedMarker.
val state=MyState()GoogleMap( googleMapOptionsFactory= {GoogleMapOptions().mapId("DEMO_MAP_ID") },//...) {val pinConfig=PinConfig.builder() .setBackgroundColor(Color.MAGENTA) .build()AdvancedMarker( state=MarkerState(position=LatLng(-34,151)), title="Magenta marker in Sydney", pinConfig= pinConfig )}
Shapes
A shape is an object on the map, tied to a latitude/longitude coordinate. Currently, android-maps-compose offersPolyline,Polygon andCircle. For all shapes, you can customize their appearance by altering a number of properties.
APolyline is a series of connected line segments that can form any shape you want and can be used to mark paths and routes on the map:
val polylinePoints= remember {listOf(singapore, singapore5) }// ...Polyline( points= polylinePoints)
You can use spans to individually color segments of a polyline, by creating StyleSpan objects:
val styleSpan=StyleSpan(StrokeStyle.gradientBuilder(Color.Red.toArgb(),Color.Green.toArgb(), ).build(),)// ...val polylinePoints= remember {listOf(singapore, singapore5) }val styleSpanList= remember {listOf(styleSpan) }// ...Polyline( points= polylinePoints, spans= styleSpanList,)
APolygon is an enclosed shape that can be used to mark areas on the map:
val polygonPoints= remember {listOf(singapore1, singapore2, singapore3) }// ...Polygon( points= polygonPoints, fillColor=Color.Black.copy(alpha=0.5f))
A Circle is a geographically accurate projection of a circle on the Earth's surface drawn on the map:
var circleCenter by remember { mutableStateOf(singapore) }// ...Circle( center= circleCenter, fillColor=MaterialTheme.colors.secondary, strokeColor=MaterialTheme.colors.secondaryVariant, radius=1000.0,)
Recomposing elements
Markers and other elements need to be recomposed in the screen. To achieve recomposition, you can set mutable properties of state objects:
val markerState= rememberUpdatedMarkerState(position= singapore)//...LaunchedEffect(Unit) { repeat(10) { delay(5.seconds)val old= markerState.position markerState.position=LatLng(old.latitude+1.0, old.longitude+2.0) }}
In the example above, recomposition occurs asMarkerState.position is updated with different values over time, shifting the Marker around the screen.
Customizing a marker's info window
You can customize a marker's info window contents by using theMarkerInfoWindowContent element, or if you want to customize the entire infowindow, use theMarkerInfoWindow element instead. Both of these elementsaccept acontent parameter to provide your customization in a composablelambda expression.
MarkerInfoWindowContent(//...) { marker->Text(marker.title?:"Default Marker Title", color=Color.Red)}MarkerInfoWindow(//...) { marker->// Implement the custom info window hereColumn {Text(marker.title?:"Default Marker Title", color=Color.Red)Text(marker.snippet?:"Default Marker Snippet", color=Color.Red) }}
Street View
You can add a Street View given a location using theStreetView composable.
- Test whether a Street View location is valid with the the
fetchStreetViewDatautility from themaps-compose-utilslibrary.
streetViewResult= fetchStreetViewData(singapore,BuildConfig.MAPS_API_KEY)
- Once the location is confirmed valid, add a Street View composable by providing a
StreetViewPanoramaOptionsobject.
val singapore=LatLng(1.3588227,103.8742114)StreetView( streetViewPanoramaOptionsFactory= {StreetViewPanoramaOptions().position(singapore) })
Controlling the map directly (experimental)
Certain use cases may require extending theGoogleMap object to decorate / augmentthe map. It can be obtained with theMapEffect Composable.Doing so can be dangerous, as theGoogleMap object is managed by this library.
GoogleMap(// ...) {MapEffect { map->// map is the GoogleMap }}
This library provides optional utilities in themaps-compose-utils library from theMaps SDK for Android Utility Library.
The marker clustering utility helps you manage multiple markers at different zoom levels.When a user views the map at a high zoom level, the individual markers show on the map. When the user zooms out, the markers gather together into clusters, to make viewing the map easier.
TheMarkerClusteringActivity demonstrates usage.
Clustering( items= items,// Optional: Handle clicks on clusters, cluster items, and cluster item info windows onClusterClick=null, onClusterItemClick=null, onClusterItemInfoWindowClick=null,// Optional: Custom rendering for clusters clusterContent=null,// Optional: Custom rendering for non-clustered items clusterItemContent=null,)
ThefetchStreetViewData method provides functionality to check whether a location is supported in StreetView. You can avoid errors when adding a Street View panorama to an Android app by calling this metadata utility and only adding a Street View panorama if the response is OK.
Important
Be sure toenable Street View Static API on the project associated with your API key.
You can see example usagein theStreetViewActivity of the demo app:
streetViewResult= fetchStreetViewData(singapore,BuildConfig.MAPS_API_KEY)
This library also provides optional composable widgets in themaps-compose-widgets library that you can use alongside theGoogleMap composable.
This widget shows the current scale of the map in feet and meters when zoomed into the map, changing to miles and kilometers, respectively, when zooming out. ADisappearingScaleBar is also included, which appears when the zoom level of the map changes, and then disappears after a configurable timeout period.
TheScaleBarActivity demonstrates both of these, with theDisappearingScaleBar in the upper left corner and the normal baseScaleBar in the upper right:
Both versions of this widget leverage theCameraPositionState inmaps-compose and therefore are very simple to configure with their defaults:
Box(Modifier.fillMaxSize()) {GoogleMap( modifier=Modifier.fillMaxSize(), cameraPositionState= cameraPositionState ) {// ... your map composables ... }ScaleBar( modifier=Modifier .padding(top=5.dp, end=15.dp) .align(Alignment.TopEnd), cameraPositionState= cameraPositionState )// ORDisappearingScaleBar( modifier=Modifier .padding(top=5.dp, end=15.dp) .align(Alignment.TopStart), cameraPositionState= cameraPositionState )}
The colors of the text, line, and shadow are also all configurable (e.g., based onisSystemInDarkTheme() on a dark map). Similarly, theDisappearingScaleBar animations can be configured.
This library calls the MapsApiSettings.addInternalUsageAttributionId method, which helps Googleunderstand which libraries and samples are helpful to developers and is optional. Instructions foropting out of the identifier are provided inreference documentation.
Contributions are welcome and encouraged! If you'd like to contribute, send us apull request and refer to ourcode of conduct andcontributing guide.
This library uses Google Maps Platform services. Use of Google Maps Platform services through this library is subject to the Google Maps PlatformTerms of Service.
If your billing address is in the European Economic Area, effective on 8 July 2025, theGoogle Maps Platform EEA Terms of Service will apply to your use of the Services. Functionality varies by region.Learn more.
This library is not a Google Maps Platform Core Service. Therefore, the Google Maps Platform Terms of Service (e.g. Technical Support Services, Service Level Agreements, and Deprecation Policy) do not apply to the code in this library.
This library is offered via an open sourcelicense. It is not governed by the Google Maps Platform SupportTechnical Support Services Guidelines, theSLA, or theDeprecation Policy. However, any Google Maps Platform services used by the library remain subject to the Google Maps Platform Terms of Service.
This library adheres tosemantic versioning to indicate when backwards-incompatible changes are introduced. Accordingly, while the library is in version 0.x, backwards-incompatible changes may be introduced at any time.
If you find a bug, or have a feature request, pleasefile an issue on GitHub. If you would like to get answers to technical questions from other Google Maps Platform developers, ask through one of ourdeveloper community channels. If you'd like to contribute, please check thecontributing guide.
You can also discuss this library on ourDiscord server.
About
Jetpack Compose composables for the Maps SDK for Android
Topics
Resources
License
Code of conduct
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.
