Skip to content

Zoomable

Maven Central

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")
        }
    }
}