Download maps for offline use
An offline pack stores the style, tiles, fonts, and icons for one area, so the map can show that area without a network connection. Use packs when people need the map in places with no signal, such as on a hike or a flight.
Get the offline storage
Section titled “Get the offline storage”Each map runtime has one OfflineStorage. Get the one for the
default runtime with the offlineStorage extension. It also works
outside composables, such as in a view model:
val offlineStorage = DefaultMapRuntime.instance.offlineStorageDownload a region
Section titled “Download a region”OfflineStorage.create saves a new pack, and
OfflineStorage.resume starts its download:
val offlineState by offlineStorage.state.collectAsState()val pixelRatio = LocalDensity.current.densityButton( enabled = offlineState is OfflineStorageState.Ready, onClick = { scope.launch { val pack = offlineStorage.create( definition = OfflinePackDefinition.TilePyramid( styleUrl = "https://tiles.openfreemap.org/styles/liberty", bounds = BoundingBox(west = -122.46, south = 47.48, east = -122.22, north = 47.74), pixelRatio = pixelRatio, minZoom = 10.0, maxZoom = 14.0, ), metadata = "Seattle".encodeToByteArray(), ) offlineStorage.resume(pack) } },) { Text("Download Seattle")}The pack is defined by:
styleUrl: the style that the map uses. Use the same URL in the map, because MapLibre finds downloaded resources by their URL.bounds: the area to download. To download the area of a GeoJSON shape, such as a hiking route, useOfflinePackDefinition.Shapeinstead.minZoomandmaxZoom: the zoom levels to download. Each extra zoom level needs about four times as many tiles as the one before, so keepmaxZoomas low as your use case allows.pixelRatio: the screen density, so the pack has tiles for this screen.
metadata holds any bytes you want to keep with the pack. The example stores a
name to show in a list.
The button stays disabled while the storage opens its database.
List downloaded packs
Section titled “List downloaded packs”OfflineStorageState.Ready lists every pack in the database,
including packs from earlier app launches. The maplibre-compose-material3
module has OfflinePackListItem, which shows a pack’s progress and
has buttons to pause, resume, update, and delete it:
val packs = (offlineState as? OfflineStorageState.Ready)?.packs.orEmpty()Column { for (pack in packs) { key(pack) { val metadata by pack.metadata.collectAsState() OfflinePackListItem(pack = pack, offlineStorage = offlineStorage) { Text(metadata?.decodeToString() ?: "Unnamed region") } } }}A download stops when the app closes. On the next launch, an incomplete pack is
paused until you call resume again. OfflinePackListItem shows a resume
button for it.
Show download progress
Section titled “Show download progress”To build your own UI, collect OfflinePack.downloadProgress. It
updates as resources download:
@Composablefun PackProgress(pack: OfflinePack) { val progress by pack.downloadProgress.collectAsState() when (val current = progress) { is DownloadProgress.Healthy -> { val fraction = if (current.requiredResourceCount > 0) { current.completedResourceCount.toFloat() / current.requiredResourceCount } else { 0f } Column { LinearProgressIndicator(progress = { fraction }) when (current.status) { DownloadStatus.Complete -> Text("Ready to use offline") DownloadStatus.Paused -> Text("Paused") else -> Text("Downloading") } } } is DownloadProgress.Error -> Text("Download failed: ${current.message}") is DownloadProgress.TileLimitExceeded -> Text("Too many tiles; the limit is ${current.limit}") else -> Text("Waiting for status") }}DownloadProgress.Healthy counts downloaded and required resources
and tells you whether the pack is downloading, paused, or complete.
DownloadProgress.Error and
DownloadProgress.TileLimitExceeded report a problem.
Delete a pack
Section titled “Delete a pack”OfflineStorage.delete removes a pack:
@Composablefun DeletePackButton(offlineStorage: OfflineStorage, pack: OfflinePack) { val scope = rememberCoroutineScope() Button(onClick = { scope.launch { offlineStorage.delete(pack) } }) { Text("Delete") }}Show the downloaded area on a map
Section titled “Show the downloaded area on a map”You do not need to configure the map. A map from the same runtime uses the pack’s resources, so the downloaded area shows when the device has no network.