How the map composes
MapState holds one map: its base style, its camera, and the
sources and layers that you declare. MaplibreMap draws a
MapState on the screen. The state is a separate object, so code outside the
map can use it. For example, a button elsewhere on the screen can move the
camera.
The base style and your content
Section titled “The base style and your content”Every map starts from a base style. A base style is a style document, usually published by a map provider at a URL. It contains sources, which supply the data, and layers, which draw that data: roads, water, labels, and so on.
The trailing block of rememberMapState declares your own sources
and layers. The library adds them to the base style. It does not change the
layers of the base style.
val state = rememberMapState(baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty")) { val earthquakes = rememberGeoJsonSource( GeoJsonData.Uri("https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson") ) CircleLayer(id = "earthquakes", source = earthquakes, color = const(Color.Red)) }MaplibreMap(state = state)Your layers draw above the layers of the base style, in the order that you
declare them: a later layer draws above an earlier one. To put a layer between
layers of the base style, for example under the labels, wrap it in an
Anchor. Draw under the labels
compares the two layer orders.
The library adds a source to the map only while one of your layers uses it. A source that no layer uses loads no data.
Compose state changes the map
Section titled “Compose state changes the map”The content block is a composable. When state that it reads changes, the block recomposes, and the library changes the map to match.
@Composablefun EarthquakeMap(showStrongOnly: Boolean, dotColor: Color) { val state = rememberMapState(baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty")) { val earthquakes = rememberGeoJsonSource( GeoJsonData.Uri("https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson") ) CircleLayer(id = "earthquakes", source = earthquakes, color = const(dotColor)) if (showStrongOnly) { CircleLayer( id = "strong-earthquakes", source = earthquakes, filter = feature["mag"].asNumber() gte const(5f), radius = const(10.dp), color = const(Color.Yellow), ) } } MaplibreMap(state = state)}When showStrongOnly becomes true, the library adds the strong-earthquakes
layer. When it becomes false, the library removes that layer. When dotColor
changes, the library sets the new color on the existing earthquakes layer.
You do not call add or remove functions. Use if, loops, and parameters, as you
do with UI composables.
Switching the base style keeps your content
Section titled “Switching the base style keeps your content”When baseStyle changes, the map loads the new style document. The library
then adds your content to the new style, so your sources and layers stay on the
map. Style the map shows
how to switch styles.
The content block composes again from the start for each style that the map
loads. Values that you remember inside the block reset at that point, so keep
app state that must outlast a style change outside the block. An Anchor
places your layers again among the layers of the new style.
Share content between maps
Section titled “Share content between maps”To show the same sources and layers in more than one place, write them as a
composable function and call it from each content block. Here an interactive
map and a MapSnapshotter, which captures map images, show the same
earthquakes:
@Composable@MaplibreComposablefun EarthquakeLayers() { val earthquakes = rememberGeoJsonSource( GeoJsonData.Uri("https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson") ) CircleLayer(id = "earthquakes", source = earthquakes, color = const(Color.Red))}
@Composablefun EarthquakeMapAndSnapshotter() { val baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty") val state = rememberMapState(baseStyle = baseStyle) { EarthquakeLayers() } val snapshotter = remember { DefaultMapRuntime.instance.createSnapshotter(baseStyle = baseStyle) { EarthquakeLayers() } } DisposableEffect(snapshotter) { onDispose { snapshotter.close() } } MaplibreMap(state = state)}A snapshotter has no MapState, so LocalMapState is null inside
it, and shared content must work without it. A snapshotter also does not wait
for effects in the content, such as one that loads data. Pass any data that the
image needs as a parameter of the shared function.
Capture a map image shows how to capture.
Change the map with handles
Section titled “Change the map with handles”Some changes are commands, not declarations: hiding a layer of the base style,
setting the state of one feature, or reading the points inside a cluster. For
these, get a handle from mapState.style. MapStyleState.layers
returns a LayerHandle for a layer ID.
MapStyleState.sources returns a SourceHandle for a
source ID or for a source object that you declared.
This example hides the 3D buildings of the base style:
val state = rememberMapState(baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"))
// Null until the style is ready. Each loaded style publishes a new handle.val buildings3d = state.style.layers["building-3d"]LaunchedEffect(buildings3d) { buildings3d?.asMutable?.setLayoutProperty("visibility", JsonPrimitive("none"))}
MaplibreMap(state = state)A handle belongs to one loaded style. It is null until the style is ready, and after a style switch the map gives you a new handle. The example reads the handle during composition, so the effect runs again for each new style.
asMutable gives write access to layers and sources of the base style. For
layers and sources that you declared, it is null: change the declaration
instead. Handles of declared sources still set feature state and read clusters.
See Query and highlight features and
Cluster points.
Layer and source IDs
Section titled “Layer and source IDs”MapLibre identifies each layer by a text ID, and you choose the ID of each layer
that you declare. The ID must be different from every other layer on the map,
including the layers of the base style. See Layer for what happens
when an ID repeats. Keep each ID the same across recompositions: a different ID
is a different layer, so the library removes the old layer and adds a new one.
Other functions find layers by ID. An Anchor can name a layer of the base
style, MapState.queryRenderedFeatures can return features from
only some layers, and mapState.style.layers returns a handle by ID.
Sources that you remember get their ID from the library, so refer to them by
the source object. To draw data from a source of the base style, get it by its
ID with getBaseSource.