I want to dynamically enable and disable scrolling programmatically in a LazyColumn
.
There don't seem to be any relevant functions on LazyListState
or relevant parameters on LazyColumn
itself. How can I achieve this in Compose?
We can make the Column scrollable by using the verticalScroll() modifier.
A LazyColumn is a vertically scrolling list that only composes and lays out the currently visible items. It's similar to a Recyclerview in the classic Android View system.
In android ScrollView there is no way or method to disable scrolling. but sometime if you want to disable scrolling of ScrollView while you are inside inner element, like if you want to disable ScrollView when you are inside Map view. So you can scroll Map view easily if you put Map view inside ScrollView.
Since 1.2.0-alpha01 userScrollEnabled
was added to LazyColumn
, LazyRow
, and LazyVerticalGrid
Answer for 1.1.0 and earlier versions:
@Ryan's solution will also disable programmatically-called scrolling.
Here's a solution proposed by a maintainer in this feature request. It'll disable scrolling, allow programmatic scrolling as well as children view touches.
private val VerticalScrollConsumer = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource) = available.copy(x = 0f)
override suspend fun onPreFling(available: Velocity) = available.copy(x = 0f)
}
private val HorizontalScrollConsumer = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource) = available.copy(y = 0f)
override suspend fun onPreFling(available: Velocity) = available.copy(y = 0f)
}
fun Modifier.disabledVerticalPointerInputScroll(disabled: Boolean = true) =
if (disabled) this.nestedScroll(VerticalScrollConsumer) else this
fun Modifier.disabledHorizontalPointerInputScroll(disabled: Boolean = true) =
if (disabled) this.nestedScroll(HorizontalScrollConsumer) else this
Usage:
LazyColumn(
modifier = Modifier.disabledVerticalPointerInputScroll()
) {
// ...
}
There's not (currently) a built-in way to do this, which is a reasonable feature request.
However, the scroll
API is flexible enough that we can add it ourselves. Basically, we create a never-ending fake scroll at MutatePriority.PreventUserInput
to prevent scrolling, and then use a do-nothing scroll at the same priority to cancel the first "scroll" and re-enable scrolling.
Here are two utility functions on LazyListState
to disable/re-enable scrolling, and a demo of them both in action (some imports will be required, but Android Studio should suggest them for you).
Note that because we're taking control of scrolling to do this, calling reenableScrolling
will also cancel any ongoing scrolls or flings (that is, you should only call it when scrolling is disabled and you want to re-enable it, not just to confirm that it's enabled).
fun LazyListState.disableScrolling(scope: CoroutineScope) {
scope.launch {
scroll(scrollPriority = MutatePriority.PreventUserInput) {
// Await indefinitely, blocking scrolls
awaitCancellation()
}
}
}
fun LazyListState.reenableScrolling(scope: CoroutineScope) {
scope.launch {
scroll(scrollPriority = MutatePriority.PreventUserInput) {
// Do nothing, just cancel the previous indefinite "scroll"
}
}
}
@Composable
fun StopScrollDemo() {
val scope = rememberCoroutineScope()
val state = rememberLazyListState()
Column {
Row {
Button(onClick = { state.disableScrolling(scope) }) { Text("Disable") }
Button(onClick = { state.reenableScrolling(scope) }) { Text("Re-enable") }
}
LazyColumn(Modifier.fillMaxWidth(), state = state) {
items((1..100).toList()) {
Text("$it", fontSize = 24.sp)
}
}
}
}
NestedScrollConnection allows you to consume any scroll applied to a lazy column or row. When true, all of the available scroll is consumed. If false, none is consumed and scrolling happens normally. With this information, you can see how this can be extended for slow/fast scrolls by returning the offset multiple by some factor.
fun Modifier.scrollEnabled(
enabled: Boolean,
) = nestedScroll(
connection = object : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource
): Offset = if(enabled) Offset.Zero else available
}
)
it can be used like this:
LazyColumn(
modifier = Modifier.scrollEnabled(
enabled = enabled, //provide a mutable state boolean here
)
){
...
However, this does block programmatic scrolls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With