Skip to content
Snippets Groups Projects
Unverified Commit 1f91dd0b authored by AC5636's avatar AC5636 :ghost:
Browse files

Add siwpe gesture to edit and delete shopping item

parent 21eb2d6d
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
......
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
......
package com.example.e13roomshoppinglist package com.example.e13roomshoppinglist
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
...@@ -14,31 +19,46 @@ import androidx.compose.foundation.layout.width ...@@ -14,31 +19,46 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissState
import androidx.compose.material3.DismissValue
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.material3.rememberDismissState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ShoppingListScreen( fun ShoppingListScreen(
state: ShoppingListState, state: ShoppingListState,
...@@ -50,7 +70,10 @@ fun ShoppingListScreen( ...@@ -50,7 +70,10 @@ fun ShoppingListScreen(
FloatingActionButton(onClick = { FloatingActionButton(onClick = {
onEvent(ShoppingListEvent.OpenAddingNewShoppingListItemDialog) onEvent(ShoppingListEvent.OpenAddingNewShoppingListItemDialog)
}) { }) {
Icon(imageVector = Icons.Default.Add, contentDescription = "Add new shopping list item") Icon(
imageVector = Icons.Default.Add,
contentDescription = "Add new shopping list item"
)
} }
}, },
modifier = Modifier.padding(16.dp) modifier = Modifier.padding(16.dp)
...@@ -84,45 +107,86 @@ fun ShoppingListScreen( ...@@ -84,45 +107,86 @@ fun ShoppingListScreen(
} }
} }
} }
Divider(thickness = 2.dp, color = Color.LightGray, modifier = Modifier.padding(bottom = 8.dp)) Divider(
thickness = 2.dp,
color = Color.LightGray,
modifier = Modifier.padding(bottom = 8.dp)
)
LazyColumn( LazyColumn(
contentPadding = padding, contentPadding = padding,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
items(state.shoppingListItems) { shoppingItem -> items(state.shoppingListItems, key = {shoppingListItem -> if (shoppingListItem.id!==null) shoppingListItem.id else 0}) { shoppingItem ->
Row(modifier = Modifier.fillMaxWidth()) { val dismissState = rememberDismissState(
Column(modifier = Modifier.weight(1f)) { confirmValueChange = {
Text( when (it) {
text = "${shoppingItem.name} - 🧮 ${shoppingItem.count}", DismissValue.DismissedToEnd -> {
fontSize = 18.sp onEvent(ShoppingListEvent.DeleteShoppingListItem(shoppingItem))
) onShowToast("Deleted ${shoppingItem.name} shopping list item via swipe gesture")
Text(text = "💶 ${shoppingItem.price}", fontSize = 16.sp) true
} }
DismissValue.DismissedToStart -> {
onEvent(
ShoppingListEvent.OpenEditingShoppingListItem(
shoppingItem
)
)
onShowToast("Opened ${shoppingItem.name} shopping list item edit dialog via swipe gesture")
false
}
else -> {
onShowToast("Swipe gesture aborted")
false
}
}
})
SwipeToDismiss(
state = dismissState,
background = { SwipeBackground(dismissState = dismissState) },
dismissContent = {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(10.dp))
.background(Color.LightGray)
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = "${shoppingItem.name} - 🧮 ${shoppingItem.count}",
fontSize = 18.sp
)
Text(text = "💶 ${shoppingItem.price}", fontSize = 16.sp)
}
IconButton(onClick = { IconButton(onClick = {
onEvent(ShoppingListEvent.DeleteShoppingListItem(shoppingItem)) onEvent(ShoppingListEvent.DeleteShoppingListItem(shoppingItem))
.also { .also {
onShowToast("Deleted shopping list item ${shoppingItem.name}") onShowToast("Deleted shopping list item ${shoppingItem.name}")
}
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete Shopping List Item"
)
} }
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete Shopping List Item"
)
}
IconButton(onClick = { IconButton(onClick = {
onEvent(ShoppingListEvent.OpenEditingShoppingListItem(shoppingItem)) onEvent(
}) { ShoppingListEvent.OpenEditingShoppingListItem(
Icon( shoppingItem
imageVector = Icons.Default.Edit, )
contentDescription = "Edit Shopping List Item" )
) }) {
} Icon(
} imageVector = Icons.Default.Edit,
Divider(thickness = 1.dp, color = Color.LightGray) contentDescription = "Edit Shopping List Item"
)
}
}
})
} }
item { item {
Row( Row(
...@@ -131,7 +195,11 @@ fun ShoppingListScreen( ...@@ -131,7 +195,11 @@ fun ShoppingListScreen(
.height(70.dp), .height(70.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text(text = "Empty item to fill up room so that the last item's edit and delete button does not go under FAB.", fontSize = 12.sp, color = Color.Gray) Text(
text = "Empty item to fill up room so that the last item's edit and delete button does not go under FAB.",
fontSize = 12.sp,
color = Color.Gray
)
} }
} }
} }
...@@ -200,15 +268,15 @@ fun AddShoppingListItemDialog( ...@@ -200,15 +268,15 @@ fun AddShoppingListItemDialog(
}, },
confirmButton = { confirmButton = {
Button(onClick = { Button(onClick = {
if (state.newShoppingListItem.name.length<3){ if (state.newShoppingListItem.name.length < 3) {
onShowToast("Item Name is too short!") onShowToast("Item Name is too short!")
return@Button return@Button
} }
if (state.newShoppingListItem.count.toIntOrNull()===null){ if (state.newShoppingListItem.count.toIntOrNull() === null) {
onShowToast("Invalid value for Count! Failed to convert value to Int.") onShowToast("Invalid value for Count! Failed to convert value to Int.")
return@Button return@Button
} }
if (state.newShoppingListItem.price.toFloatOrNull()===null){ if (state.newShoppingListItem.price.toFloatOrNull() === null) {
onShowToast("Invalid value for Price! Failed to convert value to Float.") onShowToast("Invalid value for Price! Failed to convert value to Float.")
return@Button return@Button
} }
...@@ -216,7 +284,11 @@ fun AddShoppingListItemDialog( ...@@ -216,7 +284,11 @@ fun AddShoppingListItemDialog(
if (state.isAddingNewShoppingListItem) { if (state.isAddingNewShoppingListItem) {
onEvent(ShoppingListEvent.InsertNewShoppingListItemToDb).also { onShowToast("Added new item to shopping list!") } onEvent(ShoppingListEvent.InsertNewShoppingListItemToDb).also { onShowToast("Added new item to shopping list!") }
} else if (state.isEditingShoppingListItem) { } else if (state.isEditingShoppingListItem) {
onEvent(ShoppingListEvent.UpdateCurrentlyEditingShoppingListItem).also { onShowToast("Updated shopping list item ${state.currentlyEditingShoppingListItem?.name}!") } onEvent(ShoppingListEvent.UpdateCurrentlyEditingShoppingListItem).also {
onShowToast(
"Updated shopping list item ${state.currentlyEditingShoppingListItem?.name}!"
)
}
} }
}) { }) {
Text(text = if (state.isEditingShoppingListItem) "Update" else "Insert") Text(text = if (state.isEditingShoppingListItem) "Update" else "Insert")
...@@ -234,4 +306,43 @@ fun AddShoppingListItemDialog( ...@@ -234,4 +306,43 @@ fun AddShoppingListItemDialog(
} }
} }
) )
} }
\ No newline at end of file
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun SwipeBackground(dismissState: DismissState) {
val direction = dismissState.dismissDirection ?: return
val color by animateColorAsState(
when (dismissState.targetValue) {
DismissValue.Default -> Color.Transparent
DismissValue.DismissedToEnd -> Color.Red
DismissValue.DismissedToStart -> Color.Green
}, label = ""
)
val alignment = when (direction) {
DismissDirection.StartToEnd -> Alignment.CenterStart
DismissDirection.EndToStart -> Alignment.CenterEnd
}
val icon = when (direction) {
DismissDirection.StartToEnd -> Icons.Filled.Delete
DismissDirection.EndToStart -> Icons.Filled.Edit
}
val scale by animateFloatAsState(
if (dismissState.targetValue == DismissValue.Default) 0.8f else 1.25f, label = ""
)
Box(
Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Icon(
icon,
contentDescription = "Delete",
modifier = Modifier.scale(scale)
)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment