Skip to content

Commit

Permalink
feat: Improve text fields and FABs
Browse files Browse the repository at this point in the history
Improved the keyboard actions for text fields, making them more user-friendly.
Started working on entry animations for FABs to provide a better user experience.
Added some small code improvements to enhance overall code quality.
  • Loading branch information
Mihai-Cristian Condrea committed Nov 23, 2024
1 parent 12bc776 commit 70e530f
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 125 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Version 1.1.2:

- **Minor**: Text fields now offer a more intuitive keyboard experience.
- **Minor**: Floating action buttons (FABs) have subtle entry animations for a smoother visual flow.

# Version 1.1.1:

- **New**: Added entry animations for carts and cart items.
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ android {
applicationId = "com.d4rk.cartcalculator"
minSdk = 23
targetSdk = 35
versionCode = 69
versionCode = 70
versionName = "1.1.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
resourceConfigurations += listOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.d4rk.cartcalculator.ui.components

import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import com.d4rk.cartcalculator.ui.components.animations.bounceClick

@Composable
fun AnimatedExtendedFloatingActionButton(
visible: Boolean = true,
onClick: () -> Unit,
icon: @Composable () -> Unit,
text: (@Composable () -> Unit)? = null,
) {
var isInitiallyVisible by rememberSaveable { mutableStateOf(false) }

SideEffect {
if (!isInitiallyVisible) {
isInitiallyVisible = true
}
}

val animationSpec = tween<Float>(
durationMillis = 300, delayMillis = 0, easing = FastOutSlowInEasing
)

val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f, animationSpec = animationSpec, label = "Alpha"
)
val offsetX by animateFloatAsState(
targetValue = if (visible) 0f else 50f, animationSpec = animationSpec, label = "OffsetX"
)
val offsetY by animateFloatAsState(
targetValue = if (visible) 0f else 50f, animationSpec = animationSpec, label = "OffsetY"
)
val scale by animateFloatAsState(
targetValue = if (visible) 1f else 0.8f, animationSpec = animationSpec, label = "Scale"
)

ExtendedFloatingActionButton(
onClick = onClick,
icon = icon,
text = text ?: {},
modifier = Modifier
.bounceClick()
.graphicsLayer {
this.alpha = alpha
this.translationX = offsetX
this.translationY = offsetY
this.scaleX = scale
this.scaleY = scale
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.d4rk.cartcalculator.ui.components.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
Expand All @@ -17,7 +18,9 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import com.d4rk.cartcalculator.R
Expand All @@ -43,35 +46,46 @@ fun AddNewCartAlertDialog(onDismiss : () -> Unit , onCartCreated : (ShoppingCart
onDismiss()
}
}) {
Text(text =stringResource(android.R.string.ok))
Text(text = stringResource(android.R.string.ok))
}
} ,
dismissButton = {
TextButton(onClick = {
onDismiss()
}) {
Text(text =stringResource(android.R.string.cancel))
Text(text = stringResource(android.R.string.cancel))
}
})
}

@Composable
fun AddNewCartAlertDialogContent(
newCart : MutableState<ShoppingCartTable?> , nameText : MutableState<String> ,
newCart : MutableState<ShoppingCartTable?> ,
nameText : MutableState<String> ,
) {
val currentDate = Date()
val defaultName = stringResource(R.string.shopping_cart)
val defaultName = stringResource(id = R.string.shopping_cart)

val keyboardController = LocalSoftwareKeyboardController.current

Column {
OutlinedTextField(value = nameText.value ,
onValueChange = { nameText.value = it } ,
label = { Text(text =stringResource(id = R.string.cart_name)) } ,
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences) ,
placeholder = { Text(text =stringResource(R.string.shopping_cart)) })
label = { Text(text = stringResource(id = R.string.cart_name)) } ,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences ,
imeAction = ImeAction.Done
) ,
keyboardActions = KeyboardActions(onDone = {
newCart.value =
ShoppingCartTable(name = nameText.value.ifEmpty { defaultName } ,
date = currentDate)
keyboardController?.hide()
}) ,
placeholder = { Text(text = stringResource(id = R.string.shopping_cart)) })
Spacer(modifier = Modifier.height(24.dp))
Icon(imageVector = Icons.Outlined.Info , contentDescription = null)
Spacer(modifier = Modifier.height(12.dp))
Text(text =stringResource(R.string.summary_cart_dialog))
Text(text = stringResource(id = R.string.summary_cart_dialog))
}
newCart.value =
ShoppingCartTable(name = nameText.value.ifEmpty { defaultName } , date = currentDate)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.d4rk.cartcalculator.ui.components.dialogs
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
Expand All @@ -17,7 +18,11 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
Expand All @@ -26,76 +31,102 @@ import com.d4rk.cartcalculator.data.database.table.ShoppingCartItemsTable

@Composable
fun AddNewCartItemAlertDialog(
cartId: Int, onDismiss: () -> Unit, onCartCreated: (ShoppingCartItemsTable) -> Unit
cartId : Int , onDismiss : () -> Unit , onCartCreated : (ShoppingCartItemsTable) -> Unit
) {
val newCartItem = remember { mutableStateOf<ShoppingCartItemsTable?>(null) }
AlertDialog(onDismissRequest = onDismiss, text = {
AddNewCartItemAlertDialogContent(cartId, newCartItem)
}, icon = {
AlertDialog(onDismissRequest = onDismiss , text = {
AddNewCartItemAlertDialogContent(cartId , newCartItem)
} , icon = {
Icon(
Icons.Outlined.ShoppingBag, contentDescription = null
Icons.Outlined.ShoppingBag , contentDescription = null
)
}, confirmButton = {
} , confirmButton = {
TextButton(onClick = {
newCartItem.value?.let { cartItem ->
onCartCreated(cartItem)
}
}, enabled = newCartItem.value != null) {
Text(text =stringResource(android.R.string.ok))
} , enabled = newCartItem.value != null) {
Text(text = stringResource(android.R.string.ok))
}
}, dismissButton = {
} , dismissButton = {
TextButton(onClick = {
onDismiss()
}) {
Text(text =stringResource(android.R.string.cancel))
Text(text = stringResource(android.R.string.cancel))
}
})
}

@Composable
fun AddNewCartItemAlertDialogContent(cartId: Int, newCartItem: MutableState<ShoppingCartItemsTable?>) {
val nameText = remember { mutableStateOf("") }
val priceText = remember { mutableStateOf("") }
val quantityText = remember { mutableStateOf("") }
fun AddNewCartItemAlertDialogContent(
cartId : Int ,
newCartItem : MutableState<ShoppingCartItemsTable?>
) {
val nameText = remember { mutableStateOf(value = "") }
val priceText = remember { mutableStateOf(value = "") }
val quantityText = remember { mutableStateOf(value = "") }

val nameFocusRequester = remember { FocusRequester() }
val priceFocusRequester = remember { FocusRequester() }
val quantityFocusRequester = remember { FocusRequester() }
val keyboardController = LocalSoftwareKeyboardController.current

Column {
OutlinedTextField(value = nameText.value,
onValueChange = {
nameText.value = it
},
label = { Text(stringResource(id = R.string.item_name)) },
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences),
placeholder = { Text(stringResource(id = R.string.enter_item_name)) })
OutlinedTextField(value = priceText.value,
onValueChange = { priceText.value = it },
label = { Text(stringResource(id = R.string.item_price)) },
placeholder = { Text(stringResource(id = R.string.enter_item_price)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
OutlinedTextField(value = quantityText.value,
onValueChange = { quantityText.value = it },
label = { Text(stringResource(id = R.string.quantity)) },
placeholder = { Text(stringResource(id = R.string.enter_quantity)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
OutlinedTextField(value = nameText.value ,
onValueChange = { nameText.value = it } ,
label = { Text(text = stringResource(id = R.string.item_name)) } ,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences ,
imeAction = ImeAction.Next
) ,
keyboardActions = KeyboardActions(onNext = { priceFocusRequester.requestFocus() }) ,
placeholder = { Text(text = stringResource(id = R.string.enter_item_name)) } ,
modifier = Modifier.focusRequester(nameFocusRequester))

OutlinedTextField(value = priceText.value ,
onValueChange = { priceText.value = it } ,
label = { Text(text = stringResource(id = R.string.item_price)) } ,
placeholder = { Text(text = stringResource(id = R.string.enter_item_price)) } ,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number , imeAction = ImeAction.Next
) ,
keyboardActions = KeyboardActions(onNext = { quantityFocusRequester.requestFocus() }) ,
modifier = Modifier.focusRequester(priceFocusRequester))

OutlinedTextField(value = quantityText.value ,
onValueChange = { quantityText.value = it } ,
label = { Text(text = stringResource(id = R.string.quantity)) } ,
placeholder = { Text(text = stringResource(id = R.string.enter_quantity)) } ,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number , imeAction = ImeAction.Done
) ,
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
}) ,
modifier = Modifier.focusRequester(quantityFocusRequester))

Spacer(modifier = Modifier.height(24.dp))
Icon(imageVector = Icons.Outlined.Info, contentDescription = null)
Icon(imageVector = Icons.Outlined.Info , contentDescription = null)
Spacer(modifier = Modifier.height(12.dp))
Text(stringResource(id = R.string.dialog_info_cart_item))
}

if (nameText.value.isNotEmpty() && priceText.value.isNotEmpty() && quantityText.value.isNotEmpty()) {
val price = priceText.value.replace(',', '.').toDoubleOrNull()
val price = priceText.value.replace(oldChar = ',' , newChar = '.').toDoubleOrNull()
val quantity = quantityText.value.toIntOrNull()
if (price != null && quantity != null) {
newCartItem.value = ShoppingCartItemsTable(
cartId = cartId,
name = nameText.value,
price = price.toString(),
cartId = cartId ,
name = nameText.value ,
price = price.toString() ,
quantity = quantity
)
} else {
}
else {
newCartItem.value = null
}
} else {
}
else {
newCartItem.value = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fun DeleteCartAlertDialog(
AlertDialog(onDismissRequest = {
onDismiss() } , title = {
Text(
text = stringResource(R.string.delete_cart_title) ,
text = stringResource(id= R.string.delete_cart_title) ,
)
} , text = { DeleteCartAlertDialogContent(cart) } , confirmButton = {
TextButton(onClick = {
Expand Down Expand Up @@ -56,14 +56,14 @@ fun DeleteCartAlertDialogContent(cart : ShoppingCartTable?) {
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = stringResource(R.string.delete_cart_message) ,
text = stringResource(id= R.string.delete_cart_message) ,
style = MaterialTheme.typography.bodyLarge
)
Spacer(modifier = Modifier.height(24.dp))
Icon(imageVector = Icons.Outlined.Info , contentDescription = null)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(R.string.delete_cart_warning , cart?.name ?: "") ,
text = stringResource(id= R.string.delete_cart_warning , cart?.name ?: "") ,
style = MaterialTheme.typography.bodyLarge
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fun DeleteCartItemAlertDialog(
onDismissRequest = onDismiss,
title = {
Text(
text = stringResource(R.string.delete_cart_item_title),
text = stringResource(id= R.string.delete_cart_item_title),
)
},
text = { DeleteCartItemAlertDialogContent(cartItem!!) },
Expand Down Expand Up @@ -60,14 +60,14 @@ fun DeleteCartItemAlertDialogContent(cartItem: ShoppingCartItemsTable) {
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = stringResource(R.string.delete_cart_item_message),
text = stringResource(id= R.string.delete_cart_item_message),
style = MaterialTheme.typography.bodyLarge,
)
Spacer(modifier = Modifier.height(24.dp))
Icon(imageVector = Icons.Outlined.Info, contentDescription = null)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = stringResource(R.string.delete_cart_item_warning, cartItem.name),
text = stringResource(id= R.string.delete_cart_item_warning, cartItem.name),
style = MaterialTheme.typography.bodyLarge
)
}
Expand Down
Loading

0 comments on commit 70e530f

Please sign in to comment.