Do not show floating action buttons when reordering items
This commit is contained in:
parent
b30b282628
commit
5b47484a40
10 changed files with 142 additions and 120 deletions
|
@ -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)
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
) {
|
||||
|
|
|
@ -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)
|
||||
) {
|
||||
|
|
|
@ -277,6 +277,7 @@ fun LocalPlaylistSongs(
|
|||
FloatingActionsContainerWithScrollToTop(
|
||||
lazyListState = lazyListState,
|
||||
iconId = R.drawable.shuffle,
|
||||
visible = !reorderingState.isDragging,
|
||||
onClick = {
|
||||
playlistWithSongs?.songs?.let { songs ->
|
||||
if (songs.isNotEmpty()) {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue