Zoomable¶

The landscapist-zoomable package provides a ZoomablePlugin that enables zoom and pan gestures for images. This plugin supports both Android and Kotlin Multiplatform (iOS, Desktop).
Try it in a browser
Zoomable is a switch in the playground. Tiling is off there, because a browser cannot decode a region, so it pans and zooms over the one decoded bitmap.
To use zoomable supports, add the dependency below:
dependencies {
implementation("com.github.skydoves:landscapist-zoomable:$version")
}
ZoomablePlugin¶
You can implement zoom and pan gestures by adding ZoomablePlugin to your image component:
GlideImage(
imageModel = { imageUrl },
component = rememberImageComponent {
+ZoomablePlugin()
}
)
CoilImage(
imageModel = { imageUrl },
component = rememberImageComponent {
+ZoomablePlugin()
}
)
ZoomableState¶
You can create and remember a ZoomableState with rememberZoomableState to customize the zoom behavior and access the current transformation state:
val zoomableState = rememberZoomableState(
config = ZoomableConfig(
minZoom = 1f, // Minimum zoom scale (default: 1f)
maxZoom = 4f, // Maximum zoom scale (default: 5f)
doubleTapZoom = 2f, // Zoom scale on double-tap (default: 2.5f)
enableDoubleTapZoom = true, // Enable double-tap to zoom (default: true)
)
)
GlideImage(
imageModel = { imageUrl },
component = rememberImageComponent {
+ZoomablePlugin(state = zoomableState)
}
)
// Access current zoom state
val currentScale = zoomableState.transformation.scale
val currentOffset = zoomableState.transformation.offset
You can also use resetKey parameter to automatically reset the zoom state when the image changes:
val zoomableState = rememberZoomableState(resetKey = imageUrl)
GlideImage(
imageModel = { imageUrl },
component = rememberImageComponent {
+ZoomablePlugin(state = zoomableState)
}
)
ZoomableConfig Options¶
| Parameter | Type | Default | Description |
|---|---|---|---|
minZoom |
Float | 1f | The minimum zoom scale |
maxZoom |
Float | 5f | The maximum zoom scale |
doubleTapZoom |
Float | 2.5f | The zoom scale to apply when double-tapping |
enableDoubleTapZoom |
Boolean | true | Whether double-tap to zoom gesture is enabled |
enableSubSampling |
Boolean | false | Whether sub-sampling for large images is enabled |
subSamplingConfig |
SubSamplingConfig | SubSamplingConfig() | Configuration for sub-sampling behavior |
Gestures¶
The ZoomablePlugin supports the following gestures:
- Pinch to zoom: Use two fingers to zoom in/out
- Double-tap to zoom: Double-tap to toggle between original and zoomed state
- Pan: Drag to pan around when zoomed in
Sub-Sampling¶
For very large images, Landscapist supports sub-sampling to efficiently display high-resolution images without running out of memory. Once the image is zoomed in, it draws only the visible tiles at the resolution the zoom calls for, instead of holding the whole picture at full size.
At rest, and until the image is zoomed past 1.5x, what is on screen is the ordinary decode, with whatever ImagePlugins the caller installed painted onto it. The tiles are prepared underneath and take over above that zoom. Nothing is added to or removed from the composition when they do, so an animation running on the image is not restarted by a pinch.
Enabling Sub-Sampling¶
val zoomableState = rememberZoomableState(
config = ZoomableConfig(
enableSubSampling = true,
subSamplingConfig = SubSamplingConfig(
tileSize = 512.dp, // Size of each tile (default: 256.dp)
threshold = 2000.dp, // Minimum image dimension to enable it (default: 1024.dp)
)
)
)
GlideImage(
imageModel = { imageUrl },
component = rememberImageComponent {
+ZoomablePlugin(state = zoomableState)
}
)
SubSamplingConfig Options¶
| Parameter | Type | Default | Description |
|---|---|---|---|
tileSize |
Dp | 256.dp | The size of each tile. Larger tiles mean fewer tiles but more memory per tile |
threshold |
Dp | 1024.dp | The minimum image dimension to enable sub-sampling. Images smaller than this will be rendered normally |
Sub-Sampling Support by Image Loader¶
| Image Loader | Android | iOS/Desktop |
|---|---|---|
| Coil3 | Supported (network + local) | Supported (network + local) |
| Glide | Supported (network + local) | N/A (Android only) |
Note
Sub-sampling requires the image source to support region decoding. For network images, the image is first cached to disk before sub-sampling can be used.
Kotlin Multiplatform Support¶
The ZoomablePlugin supports Kotlin Multiplatform:
- Android: Full support with sub-sampling
- iOS: Full support with sub-sampling (using CGImageSource)
- Desktop (JVM): Full support with sub-sampling
Add the dependency to your common source set:
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.github.skydoves:landscapist-zoomable:$version")
}
}
}