So, I'm in need of a layout which should be freely scrollable(I.e scroll horizontally and vertically) and should be zoomable(specifically zoom out).

I've implemented something like this for that:

var offset by remember { mutableStateOf(Offset.Zero) }
var zoom by remember { mutableFloatStateOf(1f) }

Column(
    modifier = Modifier
        .verticalScroll(rememberScrollState(), enabled = false)
        .horizontalScroll(rememberScrollState(), enabled = false)
        .pointerInput(Unit) {
            detectTransformGestures(
                onGesture = { centroid, pan, gestureZoom, gestureRotate ->
                    val oldScale = zoom
                    val newScale = zoom * gestureZoom
                    offset = (offset + centroid / oldScale) -
                            (centroid / newScale + pan / oldScale)
                    zoom = newScale
                }
            )
        }
        .graphicsLayer {
            translationX = -offset.x * zoom
            translationY = -offset.y * zoom
            scaleX = zoom
            scaleY = zoom
            transformOrigin = TransformOrigin(0f, 0f)
        },
    verticalArrangement = Arrangement.spacedBy(16.dp)
) {
    repeat(40) { column ->
        Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
            repeat(40) { row ->
                Box(modifier = Modifier) {
                    Text(
                        modifier = Modifier
                            .align(Alignment.Center),
                        text = "($row, $column)",
                        textAlign = TextAlign.Center
                    )
                }
            }
        }
    }
}

But, the problem is that the content goes way beyond the bounds of the screen while dragging? How can I make sure the content stays inside the bounds while zooming out and dragging?

1

There are 1 best solutions below

0
On

Find maxX and maxY by

maxX = (contentSize.width * scale - constraints.maxWidth)
                                    .coerceAtLeast(0F)
maxY = (contentSize.height * scale - constraints.maxHeight)
                                    .coerceAtLeast(0F)
offset = Offset(newOffset.x.coerceIn(-maxX, maxX),
                                newOffset.y.coerceIn(-maxY, maxY))

Here constraints is the size of screen received from BoxWithConstraints.