feat: Improve visual feedback for reorderable lists

Enhances the visual experience when reordering items in both shopping list
detail and shopping lists screens.

- Implemented animated scaling and background color changes for items
  being dragged to provide clearer visual feedback.
- Adjusted shadow behavior during dragging to be consistent across themes,
  specifically removing the shadow in dark theme for a cleaner look.
This commit is contained in:
2025-10-12 16:44:44 +02:00
parent 8cc05a2069
commit 85bd89cce1
2 changed files with 44 additions and 8 deletions

View File

@@ -33,6 +33,10 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
@@ -142,16 +146,25 @@ fun ShoppingListDetailScreen(
state = lazyListState,
) {
items(filteredItems, { it.id }) { item ->
ReorderableItem(reorderableLazyListState, key = item.id) { isDragging ->
val elevation = if (isDragging) 8.dp else 0.dp
ReorderableItem(reorderableLazyListState, key = item.id) { isDragging ->
val elevation = animateDpAsState(if (isDragging) 0.dp else 0.dp, label = "elevation")
val scale = animateFloatAsState(if (isDragging) 1.05f else 1.0f, label = "scale")
val isSelected = item == selectedItem
val backgroundColor = animateColorAsState(
if (isDragging) MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.7f)
else if (isSelected) MaterialTheme.colorScheme.surfaceVariant
else Color.Transparent, label = "backgroundColor"
)
Row(
modifier = Modifier
.fillMaxWidth()
.shadow(elevation)
.background(if (isSelected) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent)
.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) {
coroutineScope.launch {
.graphicsLayer {
scaleX = scale.value
scaleY = scale.value
}
.shadow(elevation.value)
.background(backgroundColor.value)
.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { coroutineScope.launch {
viewModel.saveShoppingListItem(item.copy(isChecked = !item.isChecked))
}
}

View File

@@ -1,5 +1,9 @@
package de.lxtools.noteshop.ui.shopping
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -37,6 +41,9 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -107,7 +114,8 @@ fun ShoppingListsScreen(
},
modifier = Modifier
.clickable { onListClick(listWithItems.shoppingList.id) },
scope = this
scope = this,
isDragging = isDragging
)
}
}
@@ -149,18 +157,33 @@ fun ShoppingListsScreen(
}
}
@Composable
fun ShoppingListCard(
listWithItems: ShoppingListWithItems,
onEditList: (ShoppingListWithItems) -> Unit,
onDeleteList: (ShoppingListWithItems) -> Unit,
modifier: Modifier = Modifier,
scope: ReorderableCollectionItemScope
scope: ReorderableCollectionItemScope,
isDragging: Boolean
) {
val elevation = animateDpAsState(if (isDragging) 0.dp else 0.dp, label = "elevation")
val scale = animateFloatAsState(if (isDragging) 1.05f else 1.0f, label = "scale")
val backgroundColor = animateColorAsState(
if (isDragging) MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.7f)
else Color.Transparent, label = "backgroundColor"
)
Card(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 4.dp, horizontal = 16.dp)
.graphicsLayer {
scaleX = scale.value
scaleY = scale.value
}
.shadow(elevation.value)
.background(backgroundColor.value)
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(