From 02f9ec4d9723cc7f6be7af570baf82eb261d8656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 5 Feb 2026 20:26:02 +0800 Subject: [PATCH] Fix rememberOverscrollEffect compat for legacy build The legacy build (API 21) uses Compose BOM 2025.01.00 where rememberOverscrollEffect() is internal and overscrollEffect parameter doesn't exist on verticalScroll/LazyColumn. --- .../connections/ConnectionDetailsScreen.kt | 6 +- .../screen/connections/ConnectionsScreen.kt | 9 +-- .../compose/screen/dashboard/GroupsCard.kt | 7 ++- .../sfa/compat/OverscrollCompat.kt | 56 +++++++++++++++++ .../sfa/compat/OverscrollCompat.kt | 63 +++++++++++++++++++ 5 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 app/src/minApi21/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt create mode 100644 app/src/minApi23/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionDetailsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionDetailsScreen.kt index 8671fd1..ef57240 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionDetailsScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionDetailsScreen.kt @@ -11,10 +11,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberOverscrollEffect import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.MoreVert @@ -46,6 +44,8 @@ import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import io.nekohasekai.libbox.Libbox import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compat.rememberOverscrollEffectCompat +import io.nekohasekai.sfa.compat.verticalScrollCompat import io.nekohasekai.sfa.compose.model.Connection import io.nekohasekai.sfa.compose.util.rememberSheetDismissFromContentOnlyIfGestureStartedAtTopModifier import java.text.SimpleDateFormat @@ -77,7 +77,7 @@ fun ConnectionDetailsScreen( modifier = modifier .fillMaxSize() .then(scrollModifier) - .verticalScroll(scrollState, overscrollEffect = if (asSheet) null else rememberOverscrollEffect()), + .verticalScrollCompat(scrollState, overscrollEffect = if (asSheet) null else rememberOverscrollEffectCompat()), ) { if (showHeader) { Row( diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt index 5946713..ee5be77 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberOverscrollEffect import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Check @@ -59,6 +58,8 @@ import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compat.LazyColumnCompat +import io.nekohasekai.sfa.compat.rememberOverscrollEffectCompat import io.nekohasekai.sfa.compose.model.Connection import io.nekohasekai.sfa.compose.model.ConnectionSort import io.nekohasekai.sfa.compose.model.ConnectionStateFilter @@ -401,7 +402,7 @@ fun ConnectionsScreen( lazyListState.firstVisibleItemIndex == 0 && lazyListState.firstVisibleItemScrollOffset == 0 } - LazyColumn( + LazyColumnCompat( modifier = modifier .fillMaxSize() @@ -556,7 +557,7 @@ fun ConnectionsScreen( else -> { val bounceBlockingConnection = rememberBounceBlockingNestedScrollConnection(lazyListState) - LazyColumn( + LazyColumnCompat( modifier = Modifier .fillMaxSize() @@ -564,7 +565,7 @@ fun ConnectionsScreen( state = lazyListState, contentPadding = PaddingValues(start = 16.dp, end = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.spacedBy(8.dp), - overscrollEffect = rememberOverscrollEffect(), + overscrollEffect = rememberOverscrollEffectCompat(), ) { items( items = uiState.connections, diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/GroupsCard.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/GroupsCard.kt index b04e96d..741f8a1 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/GroupsCard.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/GroupsCard.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberOverscrollEffect import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandMore @@ -67,6 +66,8 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import io.nekohasekai.libbox.Libbox import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compat.LazyColumnCompat +import io.nekohasekai.sfa.compat.rememberOverscrollEffectCompat import io.nekohasekai.sfa.compose.model.Group import io.nekohasekai.sfa.compose.model.GroupItem import io.nekohasekai.sfa.compose.screen.dashboard.groups.GroupsViewModel @@ -199,9 +200,9 @@ private fun GroupsCardContent( } else { Modifier.nestedScroll(rememberBounceBlockingNestedScrollConnection(lazyListState)) } - val overscrollEffect = if (asSheet) null else rememberOverscrollEffect() + val overscrollEffect = if (asSheet) null else rememberOverscrollEffectCompat() - LazyColumn( + LazyColumnCompat( modifier = modifier .fillMaxSize() diff --git a/app/src/minApi21/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt b/app/src/minApi21/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt new file mode 100644 index 0000000..6eba73c --- /dev/null +++ b/app/src/minApi21/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt @@ -0,0 +1,56 @@ +package io.nekohasekai.sfa.compat + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollableDefaults +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun rememberOverscrollEffectCompat(): Any? = null + +@Suppress("UNUSED_PARAMETER") +@Composable +fun Modifier.verticalScrollCompat( + state: ScrollState, + enabled: Boolean = true, + flingBehavior: FlingBehavior? = null, + reverseScrolling: Boolean = false, + overscrollEffect: Any? = null, +): Modifier = this.verticalScroll(state, enabled, flingBehavior, reverseScrolling) + +@Suppress("UNUSED_PARAMETER") +@Composable +fun LazyColumnCompat( + modifier: Modifier = Modifier, + state: LazyListState = rememberLazyListState(), + contentPadding: PaddingValues = PaddingValues(0.dp), + reverseLayout: Boolean = false, + verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, + horizontalAlignment: Alignment.Horizontal = Alignment.Start, + flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), + userScrollEnabled: Boolean = true, + overscrollEffect: Any? = null, + content: LazyListScope.() -> Unit, +) { + LazyColumn( + modifier = modifier, + state = state, + contentPadding = contentPadding, + reverseLayout = reverseLayout, + verticalArrangement = verticalArrangement, + horizontalAlignment = horizontalAlignment, + flingBehavior = flingBehavior, + userScrollEnabled = userScrollEnabled, + content = content, + ) +} diff --git a/app/src/minApi23/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt b/app/src/minApi23/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt new file mode 100644 index 0000000..0445aca --- /dev/null +++ b/app/src/minApi23/java/io/nekohasekai/sfa/compat/OverscrollCompat.kt @@ -0,0 +1,63 @@ +package io.nekohasekai.sfa.compat + +import androidx.compose.foundation.OverscrollEffect +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollableDefaults +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberOverscrollEffect +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun rememberOverscrollEffectCompat(): OverscrollEffect? = rememberOverscrollEffect() + +@Composable +fun Modifier.verticalScrollCompat( + state: ScrollState, + enabled: Boolean = true, + flingBehavior: FlingBehavior? = null, + reverseScrolling: Boolean = false, + overscrollEffect: OverscrollEffect? = rememberOverscrollEffect(), +): Modifier = this.verticalScroll( + state = state, + enabled = enabled, + flingBehavior = flingBehavior, + reverseScrolling = reverseScrolling, + overscrollEffect = overscrollEffect, +) + +@Composable +fun LazyColumnCompat( + modifier: Modifier = Modifier, + state: LazyListState = rememberLazyListState(), + contentPadding: PaddingValues = PaddingValues(0.dp), + reverseLayout: Boolean = false, + verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, + horizontalAlignment: Alignment.Horizontal = Alignment.Start, + flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), + userScrollEnabled: Boolean = true, + overscrollEffect: OverscrollEffect? = rememberOverscrollEffect(), + content: LazyListScope.() -> Unit, +) { + LazyColumn( + modifier = modifier, + state = state, + contentPadding = contentPadding, + reverseLayout = reverseLayout, + verticalArrangement = verticalArrangement, + horizontalAlignment = horizontalAlignment, + flingBehavior = flingBehavior, + userScrollEnabled = userScrollEnabled, + overscrollEffect = overscrollEffect, + content = content, + ) +}