Skip to content

Download maps for offline use

An offline pack holds the resources needed to show a region without a network: map tiles, the style, and other assets. The OfflineManager creates, tracks, and deletes packs.

Get the manager inside a composition:

App.kt
val offlineManager = rememberOfflineManager()

A pack is defined by a style URL, a bounding box, and a zoom range. OfflineManager.create registers the pack in a paused state; call OfflineManager.resume to start the download:

App.kt
Button(
onClick = {
scope.launch {
val pack =
offlineManager.create(
definition =
OfflinePackDefinition.TilePyramid(
styleUrl = "https://tiles.openfreemap.org/styles/liberty",
bounds = BoundingBox(west = -123.0, south = 47.0, east = -122.0, north = 48.0),
minZoom = 10,
maxZoom = 14,
),
metadata = "Seattle".encodeToByteArray(),
)
offlineManager.resume(pack)
}
}
) {
Text("Download Seattle")
}

The library does not interpret the metadata bytes. Use them to identify the pack later, for example with a display name.

OfflinePackDefinition.Shape downloads the region that covers a GeoJSON geometry instead of a bounding box.

OfflineManager.packs and OfflinePack.downloadProgress are backed by Compose state, so a composition that reads them recomposes as the download proceeds:

App.kt
for (pack in offlineManager.packs) {
val name = pack.metadata?.decodeToString() ?: "Unnamed"
when (val progress = pack.downloadProgress) {
is DownloadProgress.Healthy ->
Text("$name: ${progress.completedResourceCount} resources, ${progress.status}")
is DownloadProgress.Error -> Text("$name: ${progress.message}")
is DownloadProgress.TileLimitExceeded -> Text("$name: tile limit ${progress.limit}")
is DownloadProgress.Unknown -> Text("$name: waiting for status")
}
}

pause and resume switch a pack between the Paused and Downloading states. OfflineManager.invalidate re-checks a pack’s tiles against the server and updates the ones that changed.

OfflineManager.delete unregisters the pack and frees the resources that no remaining pack requires:

App.kt
for (pack in offlineManager.packs) {
Button(onClick = { scope.launch { offlineManager.delete(pack) } }) {
Text("Delete ${pack.metadata?.decodeToString()}")
}
}

Once a pack is downloaded, the map uses its resources automatically when the device has no network. No map configuration is required.