Fix buggy BottomSheet nested scroll connection

Thanks, Albert Chang!
This commit is contained in:
vfsfitvnm 2022-10-08 18:13:11 +02:00
parent 23dcce88ab
commit ae6babb452
5 changed files with 58 additions and 78 deletions

View file

@ -38,7 +38,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@Composable
@ -72,36 +71,7 @@ fun BottomSheet(
onDragEnd = {
val velocity = -velocityTracker.calculateVelocity().y
velocityTracker.resetTracking()
if (velocity > 250) {
state.expand()
} else if (velocity < -250) {
if (state.value < state.collapsedBound && onDismiss != null) {
state.dismiss()
onDismiss.invoke()
} else {
state.collapse()
}
} else {
val l0 = state.dismissedBound
val l1 = (state.collapsedBound - state.dismissedBound) / 2
val l2 = (state.expandedBound - state.collapsedBound) / 2
val l3 = state.expandedBound
when (state.value) {
in l0..l1 -> {
if (onDismiss != null) {
state.dismiss()
onDismiss.invoke()
} else {
state.collapse()
}
}
in l1..l2 -> state.collapse()
in l2..l3 -> state.expand()
else -> Unit
}
}
state.performFling(velocity, onDismiss)
}
)
}
@ -173,11 +143,11 @@ class BottomSheetState(
}
}
fun collapse() {
private fun collapse() {
collapse(SpringSpec())
}
fun expand() {
private fun expand() {
expand(SpringSpec())
}
@ -202,21 +172,53 @@ class BottomSheetState(
}
}
fun nestedScrollConnection(initialIsTopReached: Boolean = true): NestedScrollConnection {
return object : NestedScrollConnection {
var isTopReached = initialIsTopReached
fun performFling(velocity: Float, onDismiss: (() -> Unit)?) {
if (velocity > 250) {
expand()
} else if (velocity < -250) {
if (value < collapsedBound && onDismiss != null) {
dismiss()
onDismiss.invoke()
} else {
collapse()
}
} else {
val l0 = dismissedBound
val l1 = (collapsedBound - dismissedBound) / 2
val l2 = (expandedBound - collapsedBound) / 2
val l3 = expandedBound
when (value) {
in l0..l1 -> {
if (onDismiss != null) {
dismiss()
onDismiss.invoke()
} else {
collapse()
}
}
in l1..l2 -> collapse()
in l2..l3 -> expand()
else -> Unit
}
}
}
val preUpPostDownNestedScrollConnection
get() = object : NestedScrollConnection {
var isTopReached = false
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (isExpanded && available.y < 0) {
isTopReached = false
}
if (isTopReached) {
return if (isTopReached && available.y < 0 && source == NestedScrollSource.Drag) {
dispatchRawDelta(available.y)
return available
available
} else {
Offset.Zero
}
return Offset.Zero
}
override fun onPostScroll(
@ -228,44 +230,30 @@ class BottomSheetState(
isTopReached = consumed.y == 0f && available.y > 0
}
return Offset.Zero
return if (isTopReached && source == NestedScrollSource.Drag) {
dispatchRawDelta(available.y)
available
} else {
Offset.Zero
}
}
override suspend fun onPreFling(available: Velocity): Velocity {
if (isTopReached) {
return if (isTopReached) {
val velocity = -available.y
coroutineScope {
if (velocity > 250) {
expand()
} else if (velocity < -250) {
collapse()
} else {
val l0 = dismissedBound
val l1 = (collapsedBound - dismissedBound) / 2
val l2 = (expandedBound - collapsedBound) / 2
val l3 = expandedBound
performFling(velocity, null)
when (value) {
in l0..l1 -> collapse()
in l1..l2 -> collapse()
in l2..l3 -> expand()
else -> Unit
}
}
}
return available
available
} else {
Velocity.Zero
}
return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
isTopReached = false
return super.onPostFling(consumed, available)
return Velocity.Zero
}
}
}
}
const val expandedAnchor = 2

View file

@ -37,8 +37,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
@ -53,9 +51,9 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
import it.vfsfitvnm.vimusic.ui.components.themed.Menu
import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.DefaultDarkColorPalette
@ -89,7 +87,6 @@ fun Lyrics(
mediaMetadataProvider: () -> MediaMetadata,
durationProvider: () -> Long,
onLyricsUpdate: (Boolean, String, String) -> Unit,
nestedScrollConnectionProvider: () -> NestedScrollConnection,
modifier: Modifier = Modifier
) {
AnimatedVisibility(
@ -274,7 +271,6 @@ fun Lyrics(
text = lyrics,
style = typography.xs.center.medium.color(PureBlackColorPalette.text),
modifier = Modifier
.nestedScroll(remember { nestedScrollConnectionProvider() })
.verticalFadingEdge()
.verticalScroll(rememberScrollState())
.fillMaxWidth()

View file

@ -36,6 +36,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
@ -239,8 +240,8 @@ fun Player(
onShowLyrics = { isShowingLyrics = it },
isShowingStatsForNerds = isShowingStatsForNerds,
onShowStatsForNerds = { isShowingStatsForNerds = it },
nestedScrollConnectionProvider = layoutState::nestedScrollConnection,
modifier = modifier
.nestedScroll(layoutState.preUpPostDownNestedScrollConnection)
)
}

View file

@ -140,9 +140,7 @@ fun Queue(
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top).asPaddingValues(),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.nestedScroll(remember {
layoutState.nestedScrollConnection(reorderingState.lazyListState.firstVisibleItemIndex == 0 && reorderingState.lazyListState.firstVisibleItemScrollOffset == 0)
})
.nestedScroll(layoutState.preUpPostDownNestedScrollConnection)
) {
items(

View file

@ -21,7 +21,6 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
@ -48,7 +47,6 @@ fun Thumbnail(
onShowLyrics: (Boolean) -> Unit,
isShowingStatsForNerds: Boolean,
onShowStatsForNerds: (Boolean) -> Unit,
nestedScrollConnectionProvider: () -> NestedScrollConnection,
modifier: Modifier = Modifier
) {
val binder = LocalPlayerServiceBinder.current
@ -145,7 +143,6 @@ fun Thumbnail(
size = thumbnailSizeDp,
mediaMetadataProvider = mediaItem::mediaMetadata,
durationProvider = player::getDuration,
nestedScrollConnectionProvider = nestedScrollConnectionProvider,
)
StatsForNerds(