Has anyone written a scrollable column that doesn'...
# compose-desktop
s
Has anyone written a scrollable column that doesn't have any issues?
This is my current implementation:
Copy code
@Composable
actual fun ScrollableColumnImpl(
    modifier: Modifier,
    verticalArrangement: Arrangement.Vertical,
    horizontalAlignment: Alignment.Horizontal,
    content: @Composable ColumnScope.() -> Unit
) {
    Box(modifier) {
        val state = rememberScrollState()
        val adapter = rememberScrollbarAdapter(state)
        val isScrollbarVisible = adapter.viewportSize < adapter.contentSize
        Column(
            modifier = Modifier.fillMaxSize()
                .padding(end = if (isScrollbarVisible) LocalScrollbarStyle.current.thickness else 0.dp)
                .verticalScroll(state),
            verticalArrangement = verticalArrangement,
            horizontalAlignment = horizontalAlignment,
            content = content,
        )
        if (isScrollbarVisible) {
            VerticalScrollbar(
                modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
                adapter = adapter,
            )
        }
    }
}
I don't like the
.fillMaxSize()
, but most of my usages want it, and I can't find another way to do it. Maybe using
Layout
here would make sense.
Ok, here it is rewritten with a layout:
Copy code
@Composable
actual fun ScrollableColumnImpl(
    modifier: Modifier,
    verticalArrangement: Arrangement.Vertical,
    horizontalAlignment: Alignment.Horizontal,
    content: @Composable ColumnScope.() -> Unit
) {
    val state = rememberScrollState()
    val adapter = rememberScrollbarAdapter(state)
    val isScrollbarVisible = adapter.viewportSize < adapter.contentSize

    Layout(
        content = {
            Column(
                modifier = Modifier
                    .padding(end = if (isScrollbarVisible) LocalScrollbarStyle.current.thickness else 0.dp)
                    .verticalScroll(state),
                verticalArrangement = verticalArrangement,
                horizontalAlignment = horizontalAlignment,
                content = content,
            )
            if (isScrollbarVisible) {
                VerticalScrollbar(
                    adapter = adapter,
                )
            }
        },
        modifier = modifier,
    ) { measurables, constraints ->
        val columnPlaceable = measurables[0].measure(constraints)
        val scrollbarPlaceable = measurables.getOrNull(1)?.measure(
            Constraints(maxHeight = constraints.maxHeight, maxWidth = constraints.maxWidth)
        )
        layout(columnPlaceable.width, columnPlaceable.height) {
            columnPlaceable.place(0, 0)
            scrollbarPlaceable?.place(columnPlaceable.width - scrollbarPlaceable.width, 0)
        }
    }
}
I put my implementations for this and a lazy version here: https://gist.github.com/sproctor/297103451bdcdd25eef5b233a0d7af56
m
I didn't bother changing the spacing based on whether or not the scrollbar is visible (which can lead to jumpiness e.g. if a scrollbar being visible changes the height). Everything worked as expected for me https://github.com/UstadMobile/UstadMobile/blob/primary/lib-ui-compose/src/desktop[…]tlin/com/ustadmobile/libuicompose/components/UstadLazyColumn.kt
s
They problem is if you ever want the column to be sized based on its content, it's impossible. You should probably add a
fillMaxSize
to your Android implementation, otherwise if you don't add it where you call this, you're going to have a difference between Android and desktop.
Yeah, I think you're right about the padding. I believe all of my usages leave at least the thickness of the scrollbar. I'll have to adjust any that don't, but I think it's better to put over the existing padding. ie 16 DP of padding on Android and desktop, but the scrollbar is in that padding on desktop.
Thanks for the suggestions. I updated the gist.