This guide explains how to make a Lite Mode map in Maps Compose follow the Android system theme and switch to a dark palette automatically.
In standard (interactive) mode, Maps Compose supports dark mode directly via the mapColorScheme parameter:
GoogleMap(
mapColorScheme = ComposeMapColorScheme.FOLLOW_SYSTEM
)However, when Lite Mode is enabled (GoogleMapOptions().liteMode(true)), the underlying Google Maps Android SDK (play-services-maps) functions differently:
- Static Pre-rendered Tiles: Lite Mode maps fetch static raster tiles rather than rendering vectors on the client GPU.
MapColorSchemeBypassed: Lite Mode maps do not supportMapColorScheme, so Maps Compose does not applymapColorSchemeto them. Older versions of Google Play services throwUnsupportedOperationExceptionwhen a color scheme is set on a Lite Mode map.- Map Types Remain in Light Mode: Neither
MapType.NORMALnorMapType.TERRAINwill render dark tiles throughmapColorScheme.
To support dark mode on a Lite Mode map, your application must listen for system theme changes and dynamically supply a custom dark JSON style using MapProperties.mapStyleOptions.
Lite Mode maps support styling through standard Google Maps JSON style definitions. Define a compact dark palette constant (or store it in res/raw/map_style_dark.json):
private const val DARK_STYLE_JSON = """[
{"elementType": "geometry", "stylers": [{"color": "#242f3e"}]},
{"elementType": "labels.text.stroke", "stylers": [{"color": "#242f3e"}]},
{"elementType": "labels.text.fill", "stylers": [{"color": "#746855"}]},
{"featureType": "administrative.locality", "elementType": "labels.text.fill", "stylers": [{"color": "#d59563"}]},
{"featureType": "poi", "elementType": "labels.text.fill", "stylers": [{"color": "#d59563"}]},
{"featureType": "poi.park", "elementType": "geometry", "stylers": [{"color": "#263c3f"}]},
{"featureType": "poi.park", "elementType": "labels.text.fill", "stylers": [{"color": "#6b9a76"}]},
{"featureType": "road", "elementType": "geometry", "stylers": [{"color": "#38414e"}]},
{"featureType": "road", "elementType": "geometry.stroke", "stylers": [{"color": "#212a37"}]},
{"featureType": "road", "elementType": "labels.text.fill", "stylers": [{"color": "#9ca5b3"}]},
{"featureType": "road.highway", "elementType": "geometry", "stylers": [{"color": "#746855"}]},
{"featureType": "water", "elementType": "geometry", "stylers": [{"color": "#17263c"}]},
{"featureType": "water", "elementType": "labels.text.fill", "stylers": [{"color": "#515c6d"}]}
]"""In Jetpack Compose, isSystemInDarkTheme() observes LocalConfiguration.current and automatically triggers recomposition whenever the user changes the system theme or when an automated schedule activates.
val isSystemDark = isSystemInDarkTheme()Construct a MapProperties instance that applies MapStyleOptions conditionally based on the active dark theme state:
val mapProperties = remember(isSystemDark) {
MapProperties(
mapType = MapType.NORMAL,
mapStyleOptions = if (isSystemDark) MapStyleOptions(DARK_STYLE_JSON) else null
)
}Create the GoogleMap composable passing GoogleMapOptions().liteMode(true) via googleMapOptionsFactory:
GoogleMap(
modifier = Modifier.fillMaxSize(),
googleMapOptionsFactory = {
GoogleMapOptions().liteMode(true)
},
properties = mapProperties,
)Below is a complete, self-contained composable showing how to combine system theme observation with user overrides (System, Light, Dark):
enum class ThemeOption {
SYSTEM,
LIGHT,
DARK
}
@Composable
fun ThemedLiteMapScreen() {
var selectedTheme by rememberSaveable { mutableStateOf(ThemeOption.SYSTEM) }
val systemInDarkTheme = isSystemInDarkTheme()
val isDark = when (selectedTheme) {
ThemeOption.SYSTEM -> systemInDarkTheme
ThemeOption.LIGHT -> false
ThemeOption.DARK -> true
}
val mapProperties = remember(isDark) {
MapProperties(
mapType = MapType.NORMAL,
mapStyleOptions = if (isDark) MapStyleOptions(DARK_STYLE_JSON) else null
)
}
Column(modifier = Modifier.fillMaxSize()) {
// Theme selector
Row(
modifier = Modifier.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
FilterChip(
selected = selectedTheme == ThemeOption.SYSTEM,
onClick = { selectedTheme = ThemeOption.SYSTEM },
label = { Text("System") }
)
FilterChip(
selected = selectedTheme == ThemeOption.LIGHT,
onClick = { selectedTheme = ThemeOption.LIGHT },
label = { Text("Light") }
)
FilterChip(
selected = selectedTheme == ThemeOption.DARK,
onClick = { selectedTheme = ThemeOption.DARK },
label = { Text("Dark") }
)
}
// Lite Mode Map
Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
GoogleMap(
modifier = Modifier.fillMaxSize(),
googleMapOptionsFactory = { GoogleMapOptions().liteMode(true) },
properties = mapProperties,
)
}
}
}- Memoize Properties: Wrap
MapPropertiescreation inremember(isDark, mapType)to avoid allocating new style options on unrelated recompositions. - Preserve State Across Recreations: Use
rememberSaveablefor any user-selected theme or map settings so selections persist when the device configuration changes. - Camera Movement: In Lite Mode,
cameraPositionState.animate(...)completes immediately without continuous panning animations. - Interactive Mode Alternative: If native vector dark terrain shaders or full gesture interactions are needed, use interactive mode (
liteMode(false)) withmapColorScheme = ComposeMapColorScheme.FOLLOW_SYSTEM.
A complete, runnable demonstration of this pattern is available in the sample application: