Last active
June 3, 2025 13:01
-
-
Save f2janyway/b8dfafe995f152dcf0882587eb775665 to your computer and use it in GitHub Desktop.
ScrollBar for LazyColumn; if there is wrapper to LazyColumn(ex;Column), just use with Box() as direct above wrapper with ScrollBar and LazyColumn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.compose.animation.AnimatedVisibility | |
import androidx.compose.animation.fadeIn | |
import androidx.compose.animation.fadeOut | |
import androidx.compose.foundation.Canvas | |
import androidx.compose.foundation.layout.size | |
import androidx.compose.foundation.lazy.LazyListState | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.DisposableEffect | |
import androidx.compose.runtime.derivedStateOf | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.rememberCoroutineScope | |
import androidx.compose.runtime.setValue | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.geometry.Offset | |
import androidx.compose.ui.geometry.Size | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.unit.dp | |
import kotlinx.coroutines.Job | |
import kotlinx.coroutines.delay | |
import kotlinx.coroutines.launch | |
@Composable | |
fun ScrollBar( | |
lazyListState: LazyListState, | |
hidable: Boolean = true, | |
color: Color = Color.LightGray, | |
width: Int = 10, | |
modifier: Modifier = Modifier, | |
) { | |
val height by remember(lazyListState) { | |
derivedStateOf { | |
val columnHeight = lazyListState.layoutInfo.viewportSize.height | |
val totalCnt = lazyListState.layoutInfo.totalItemsCount.takeIf { it > 0 } ?: 1 | |
val visibleLastIndex = lazyListState.layoutInfo.visibleItemsInfo.lastIndex | |
(visibleLastIndex + 1) * (columnHeight.toFloat() / totalCnt) | |
} | |
} | |
val topOffset by remember(lazyListState) { | |
derivedStateOf { | |
val totalCnt = lazyListState.layoutInfo.totalItemsCount.takeIf { it > 0 } ?: 1 | |
val visibleCnt = | |
lazyListState.layoutInfo.visibleItemsInfo.count().takeIf { it > 0 } ?: 1 | |
val columnHeight = lazyListState.layoutInfo.viewportSize.height | |
val firstVisibleIndex = lazyListState.firstVisibleItemIndex | |
val scrollItemHeight = (columnHeight.toFloat() / totalCnt) | |
val realItemHeight = (columnHeight.toFloat() / visibleCnt) | |
val offset = ((firstVisibleIndex) * scrollItemHeight) | |
val firstItemOffset = | |
lazyListState.firstVisibleItemScrollOffset / realItemHeight * scrollItemHeight | |
offset + firstItemOffset | |
} | |
} | |
val scope = rememberCoroutineScope() | |
var isShownScrollBar by remember(lazyListState) { | |
mutableStateOf(true) | |
} | |
if (hidable) { | |
var disposeJob: Job? by remember { | |
mutableStateOf(null) | |
} | |
DisposableEffect(topOffset) { | |
isShownScrollBar = true | |
onDispose { | |
disposeJob?.takeIf { it.isActive }?.let { | |
it.cancel() | |
} | |
disposeJob = scope.launch { | |
delay(1000) | |
isShownScrollBar = false | |
} | |
} | |
} | |
} | |
val columnSize by remember(lazyListState) { | |
derivedStateOf { | |
lazyListState.layoutInfo.viewportSize | |
} | |
} | |
AnimatedVisibility(visible = isShownScrollBar, enter = fadeIn(), exit = fadeOut()) { | |
Canvas( | |
modifier = Modifier | |
.size(width = columnSize.width.dp, height = columnSize.height.dp), | |
onDraw = { | |
drawRect( | |
color, | |
topLeft = Offset(this.size.width - width, topOffset), | |
size = Size(width.toFloat(), height), | |
) | |
}) | |
} | |
} | |
@Preview | |
@Composable | |
fun ScrollBarPreview() { | |
val list = List<Int>(100) { it } | |
val lazyListState = rememberLazyListState() | |
LazyColumn( | |
state = lazyListState, modifier = Modifier | |
.fillMaxSize() | |
.background(Color.White) | |
) { | |
items(items = list) { | |
Box( | |
modifier = Modifier | |
.padding(36.dp) | |
.fillMaxWidth(), contentAlignment = Alignment.Center | |
) { | |
Text(text = "$it", color = Color.Black) | |
} | |
} | |
} | |
ScrollBar(lazyListState = lazyListState) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice