Do not show floating action buttons when reordering items

This commit is contained in:
vfsfitvnm 2022-10-06 18:01:48 +02:00
parent b30b282628
commit 5b47484a40
10 changed files with 142 additions and 120 deletions

View file

@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.LocalPlayerAwarePaddingValues
import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.utils.isScrollingDown
import it.vfsfitvnm.vimusic.utils.isScrollingDownToIsFar
import it.vfsfitvnm.vimusic.utils.ScrollingInfo
import it.vfsfitvnm.vimusic.utils.scrollingInfo
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
import kotlinx.coroutines.launch
@ -32,12 +32,13 @@ import kotlinx.coroutines.launch
fun BoxScope.FloatingActionsContainerWithScrollToTop(
lazyGridState: LazyGridState,
modifier: Modifier = Modifier,
visible: Boolean = true,
iconId: Int? = null,
onClick: (() -> Unit)? = null,
) {
val transitionState = remember {
MutableTransitionState(false to false)
}.apply { targetState = lazyGridState.isScrollingDownToIsFar() }
MutableTransitionState<ScrollingInfo?>(ScrollingInfo())
}.apply { targetState = if (visible) lazyGridState.scrollingInfo() else null }
FloatingActions(
transitionState = transitionState,
@ -53,12 +54,13 @@ fun BoxScope.FloatingActionsContainerWithScrollToTop(
fun BoxScope.FloatingActionsContainerWithScrollToTop(
lazyListState: LazyListState,
modifier: Modifier = Modifier,
visible: Boolean = true,
iconId: Int? = null,
onClick: (() -> Unit)? = null,
) {
val transitionState = remember {
MutableTransitionState(false to false)
}.apply { targetState = lazyListState.isScrollingDownToIsFar() }
MutableTransitionState<ScrollingInfo?>(ScrollingInfo())
}.apply { targetState = if (visible) lazyListState.scrollingInfo() else null }
FloatingActions(
transitionState = transitionState,
@ -74,12 +76,13 @@ fun BoxScope.FloatingActionsContainerWithScrollToTop(
fun BoxScope.FloatingActionsContainerWithScrollToTop(
scrollState: ScrollState,
modifier: Modifier = Modifier,
visible: Boolean = true,
iconId: Int? = null,
onClick: (() -> Unit)? = null,
) {
val transitionState = remember {
MutableTransitionState(false to false)
}.apply { targetState = scrollState.isScrollingDown() to false }
MutableTransitionState<ScrollingInfo?>(ScrollingInfo())
}.apply { targetState = if (visible) scrollState.scrollingInfo() else null }
FloatingActions(
transitionState = transitionState,
@ -92,13 +95,13 @@ fun BoxScope.FloatingActionsContainerWithScrollToTop(
@ExperimentalAnimationApi
@Composable
fun BoxScope.FloatingActions(
transitionState: MutableTransitionState<Pair<Boolean, Boolean>>,
transitionState: MutableTransitionState<ScrollingInfo?>,
modifier: Modifier = Modifier,
onScrollToTop: (suspend () -> Unit)? = null,
iconId: Int? = null,
onClick: (() -> Unit)? = null,
) {
val transition = updateTransition(transitionState, "FloatingActionsContainer")
val transition = updateTransition(transitionState, "")
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
@ -110,7 +113,7 @@ fun BoxScope.FloatingActions(
) {
onScrollToTop?.let {
transition.AnimatedVisibility(
visible = { it.first && it.second },
visible = { it?.isScrollingDown == false && it.isFar },
enter = slideInVertically(tween(500, if (iconId == null) 0 else 100)) { it },
exit = slideOutVertically(tween(500, 0)) { it },
) {
@ -122,6 +125,7 @@ fun BoxScope.FloatingActions(
onScrollToTop()
}
},
enabled = transition.targetState?.isScrollingDown == false && transition.targetState?.isFar == true,
iconId = R.drawable.chevron_up,
modifier = Modifier
.padding(bottom = 16.dp)
@ -132,13 +136,14 @@ fun BoxScope.FloatingActions(
iconId?.let {
onClick?.let {
transition.AnimatedVisibility(
visible = { it.first },
visible = { it?.isScrollingDown == false },
enter = slideInVertically(tween(500, 0)) { it },
exit = slideOutVertically(tween(500, 100)) { it },
) {
PrimaryButton(
iconId = iconId,
onClick = onClick,
enabled = transition.targetState?.isScrollingDown == false,
modifier = Modifier
.padding(bottom = 16.dp)
)

View file

@ -21,14 +21,14 @@ fun PrimaryButton(
onClick: () -> Unit,
@DrawableRes iconId: Int,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
enabled: Boolean = true,
) {
val (colorPalette) = LocalAppearance.current
Box(
modifier = modifier
.clip(RoundedCornerShape(16.dp))
.clickable(enabled = isEnabled, onClick = onClick)
.clickable(enabled = enabled, onClick = onClick)
.background(colorPalette.background2)
.size(62.dp)
) {

View file

@ -21,14 +21,14 @@ fun SecondaryButton(
onClick: () -> Unit,
@DrawableRes iconId: Int,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
enabled: Boolean = true,
) {
val (colorPalette) = LocalAppearance.current
Box(
modifier = modifier
.clip(CircleShape)
.clickable(enabled = isEnabled, onClick = onClick)
.clickable(enabled = enabled, onClick = onClick)
.background(colorPalette.background2)
.size(48.dp)
) {

View file

@ -277,6 +277,7 @@ fun LocalPlaylistSongs(
FloatingActionsContainerWithScrollToTop(
lazyListState = lazyListState,
iconId = R.drawable.shuffle,
visible = !reorderingState.isDragging,
onClick = {
playlistWithSongs?.songs?.let { songs ->
if (songs.isNotEmpty()) {

View file

@ -1,40 +0,0 @@
package it.vfsfitvnm.vimusic.utils
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
suspend fun LazyGridState.smoothScrollToTop() {
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
scrollToItem(layoutInfo.visibleItemsInfo.size)
}
animateScrollToItem(0)
}
@Composable
fun LazyGridState.isScrollingDownToIsFar(): Pair<Boolean, Boolean> {
var previousIndex by remember(this) {
mutableStateOf(firstVisibleItemIndex)
}
var previousScrollOffset by remember(this) {
mutableStateOf(firstVisibleItemScrollOffset)
}
return remember(this) {
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
previousIndex > firstVisibleItemIndex
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
} to (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size)
}
}.value
}

View file

@ -1,40 +0,0 @@
package it.vfsfitvnm.vimusic.utils
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
suspend fun LazyListState.smoothScrollToTop() {
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
scrollToItem(layoutInfo.visibleItemsInfo.size)
}
animateScrollToItem(0)
}
@Composable
fun LazyListState.isScrollingDownToIsFar(): Pair<Boolean, Boolean> {
var previousIndex by remember(this) {
mutableStateOf(firstVisibleItemIndex)
}
var previousScrollOffset by remember(this) {
mutableStateOf(firstVisibleItemScrollOffset)
}
return remember(this) {
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
previousIndex > firstVisibleItemIndex
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
} to (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size)
}
}.value
}

View file

@ -1,24 +0,0 @@
package it.vfsfitvnm.vimusic.utils
import androidx.compose.foundation.ScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@Composable
fun ScrollState.isScrollingDown(): Boolean {
var previousValue by remember(this) {
mutableStateOf(value)
}
return remember(this) {
derivedStateOf {
(previousValue >= value).also {
previousValue = value
}
}
}.value
}

View file

@ -0,0 +1,93 @@
package it.vfsfitvnm.vimusic.utils
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
data class ScrollingInfo(
val isScrollingDown: Boolean = false,
val isFar: Boolean = false
) {
fun and(condition: Boolean) =
// copy(isScrollingDown = isScrollingDown && condition, isFar = isFar && condition)
if (condition) this else copy(isScrollingDown = !isScrollingDown, isFar = !isFar)
}
@Composable
fun LazyListState.scrollingInfo(): ScrollingInfo {
var previousIndex by remember(this) {
mutableStateOf(firstVisibleItemIndex)
}
var previousScrollOffset by remember(this) {
mutableStateOf(firstVisibleItemScrollOffset)
}
return remember(this) {
derivedStateOf {
val isScrollingDown = if (previousIndex == firstVisibleItemIndex) {
firstVisibleItemScrollOffset > previousScrollOffset
} else {
firstVisibleItemIndex > previousIndex
}
val isFar = firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
ScrollingInfo(isScrollingDown, isFar)
}
}.value
}
@Composable
fun LazyGridState.scrollingInfo(): ScrollingInfo {
var previousIndex by remember(this) {
mutableStateOf(firstVisibleItemIndex)
}
var previousScrollOffset by remember(this) {
mutableStateOf(firstVisibleItemScrollOffset)
}
return remember(this) {
derivedStateOf {
val isScrollingDown = if (previousIndex == firstVisibleItemIndex) {
firstVisibleItemScrollOffset > previousScrollOffset
} else {
firstVisibleItemIndex > previousIndex
}
val isFar = firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
ScrollingInfo(isScrollingDown, isFar)
}
}.value
}
@Composable
fun ScrollState.scrollingInfo(): ScrollingInfo {
var previousValue by remember(this) {
mutableStateOf(value)
}
return remember(this) {
derivedStateOf {
val isScrollingDown = value > previousValue
previousValue = value
ScrollingInfo(isScrollingDown, false)
}
}.value
}

View file

@ -0,0 +1,24 @@
package it.vfsfitvnm.vimusic.utils
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
suspend fun LazyGridState.smoothScrollToTop() {
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
scrollToItem(layoutInfo.visibleItemsInfo.size)
}
animateScrollToItem(0)
}
suspend fun LazyListState.smoothScrollToTop() {
if (firstVisibleItemIndex > layoutInfo.visibleItemsInfo.size) {
scrollToItem(layoutInfo.visibleItemsInfo.size)
}
animateScrollToItem(0)
}

View file

@ -52,6 +52,9 @@ class ReorderingState(
internal var indexesToAnimate = mutableStateMapOf<Int, Animatable<Int, AnimationVector1D>>()
private var animatablesPool: AnimatablesPool<Int, AnimationVector1D>? = null
val isDragging: Boolean
get() = draggingIndex != -1
fun onDragStart(index: Int) {
overscrolled = 0
itemInfo = lazyListState.layoutInfo.visibleItemsInfo.find {