Present outside Compose UI
MaplibreMap draws a map inside Compose UI. Some screens are drawn
by another UI framework: an Android Auto car screen, a CarPlay screen, a UIKit
or SwiftUI view, or a web page built with Compose HTML, Kobweb, or React. To
show a map there, use a presentation. It draws the map into the surface, view,
or HTML element that the other framework gives you.
You still write the map’s sources, layers, and camera code in Kotlin. Create the
MapState with MapRuntime.createMapState, which takes
the same base style and content block as rememberMapState, and close the
state when you no longer need the map.
A presentation draws only the map. It has no gestures, buttons, or attribution:
- Pass gestures from your framework to
MapState.panBy,MapState.scaleBy,MapState.fling, andMapState.click. - Show the data sources’ attribution in your own UI.
state.style.attributions()returns the attribution texts as a list of HTML strings.
Android
Section titled “Android”AndroidMapPresentation draws into an Android Surface, such as the
one that Android Auto gives a car app. Connect the host’s surface callbacks to
the presentation:
/** Call every method on the main thread. */class SurfaceMapHost(context: Context, lifecycle: Lifecycle) : AutoCloseable { val state = DefaultMapRuntime.instance.createMapState( baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty") ) { // Sources and layers, as in rememberMapState } val presentation = AndroidMapPresentation(context, state, lifecycle) private var binding: AndroidMapPresentation.SurfaceBinding? = null
fun onSurfaceAvailable(surface: Surface, width: Int, height: Int, density: Float) { binding = presentation.attachSurface(surface, width, height, density) }
fun onSurfaceChanged(width: Int, height: Int, density: Float) { binding?.update(width, height, density) }
fun onSurfaceDestroyed() { binding?.close() binding = null }
fun onConfigurationChanged(configuration: Configuration) { presentation.updateConfiguration(configuration) }
override fun close() { presentation.close() state.close() }}width and height are in physical pixels, and density is physical pixels
per dp. The map stops drawing while lifecycle is below STARTED. Pass the
host’s configuration changes, so your map content sees the current night mode
and locale.
The Android Auto demo
connects these calls to the car app library’s SurfaceCallback, including
scroll and zoom gestures.
AppleMapPresentation draws the map, and
MaplibreMapView is a UIKit view that shows it:
/** Create and use this host on the main thread. */class UiKitMapHost : AutoCloseable { val state = DefaultMapRuntime.instance.createMapState( baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty") ) { // Sources and layers, as in rememberMapState } val presentation = AppleMapPresentation(state, isActive = false) val view: UIView = MaplibreMapView(presentation)
fun onSceneActivationChanged(active: Boolean) { presentation.isActive = active }
override fun close() { view.removeFromSuperview() presentation.close() state.close() }}Add view to a UIKit view hierarchy or a CarPlay window, or return it from
makeUIView in a SwiftUI UIViewRepresentable. Set isActive from the scene
that shows the map, so the map stops drawing while that scene is in the
background. A CarPlay screen has its own scene, separate from the phone’s.
The CarPlay demo
creates a Kotlin host
like this one from Swift. If you manage your own CAMetalLayer, use
AppleMapPresentation.attachLayer instead of MaplibreMapView.
WebMapPresentation draws the map into an HTML element. Give the
element a CSS width and height:
/** Give the container a CSS width and height. */@OptIn(ExperimentalMaplibreComposeApi::class)class WebMapHost(container: HTMLElement) : AutoCloseable { val state = DefaultMapRuntime.instance.createMapState( baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty") ) { // Sources and layers, as in rememberMapState } val presentation = WebMapPresentation(state) private val binding = presentation.attachContainer(container)
override fun close() { binding.close() presentation.close() state.close() }}Create the host when your framework adds the element to the page, and close it before the element is removed. The ferry departures demo does this in Compose HTML.
If your Content Security Policy blocks workers from blob: URLs, see
configureMaplibreWorker.