Showing AddAccountDialog on click on FloatingActionButton

This commit is contained in:
dankito 2024-08-25 04:27:04 +02:00
parent 23bd5833ea
commit 5f25b51487
9 changed files with 268 additions and 21 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,22 +1,22 @@
package net.codinux.banking.ui package net.codinux.banking.ui
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.*
import androidx.compose.material.MaterialTheme import androidx.compose.material.icons.Icons
import androidx.compose.material.Typography import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.ui.composables.TransactionsList import net.codinux.banking.ui.composables.TransactionsList
import net.codinux.banking.ui.dialogs.AddAccountDialog
import net.codinux.banking.ui.service.BankingService import net.codinux.banking.ui.service.BankingService
import net.codinux.banking.ui.service.Colors import net.codinux.banking.ui.service.Colors
import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.compose.ui.tooling.preview.Preview
@ -30,6 +30,10 @@ private val bankService = BankingService()
@Composable @Composable
@Preview @Preview
fun App() { fun App() {
val colors = MaterialTheme.colors.copy(secondary = Colors.CodinuxSecondaryColor, onSecondary = Color.White)
var showAddAccountDialog by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val (transactions, setTransaction) = remember { mutableStateOf<List<AccountTransaction>>(emptyList()) } val (transactions, setTransaction) = remember { mutableStateOf<List<AccountTransaction>>(emptyList()) }
@ -37,12 +41,28 @@ fun App() {
setTransaction(bankService.getTransactions()) setTransaction(bankService.getTransactions())
} }
MaterialTheme(typography = typography) { MaterialTheme(colors = colors, typography = typography) {
Box {
Column( Column(
Modifier.fillMaxWidth().fillMaxHeight().background(color = Colors.Zinc100), Modifier.fillMaxWidth().fillMaxHeight().background(color = Colors.Zinc100),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
TransactionsList(transactions) TransactionsList(transactions)
} }
Row(Modifier.align(Alignment.BottomEnd)) {
FloatingActionButton(
shape = CircleShape,
modifier = Modifier.offset((-12).dp, (-12).dp),
onClick = { showAddAccountDialog = true }
) {
Icon(Icons.Filled.Add, contentDescription = "Add a bank account")
}
}
if (showAddAccountDialog) {
AddAccountDialog { showAddAccountDialog = false }
}
}
} }
} }

View File

@ -4,8 +4,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
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.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Divider import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
@ -23,6 +21,7 @@ import kotlinx.datetime.LocalDate
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.ui.extensions.toBigDecimal import net.codinux.banking.ui.extensions.toBigDecimal
import net.codinux.banking.ui.forms.RoundedCornersCard
import net.codinux.banking.ui.service.Colors import net.codinux.banking.ui.service.Colors
import net.codinux.banking.ui.service.DI import net.codinux.banking.ui.service.DI
import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.compose.ui.tooling.preview.Preview
@ -51,10 +50,7 @@ fun TransactionsList(transactions: List<AccountTransaction>) {
val monthTransactions = groupedByMonth[month].orEmpty().sortedByDescending { it.valueDate } val monthTransactions = groupedByMonth[month].orEmpty().sortedByDescending { it.valueDate }
Card( RoundedCornersCard {
shape = RoundedCornerShape(12.dp), // Rounded corners
elevation = 2.dp // Shadow elevation
) {
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
monthTransactions.forEachIndexed { index, transaction -> monthTransactions.forEachIndexed { index, transaction ->
TransactionListItem(transaction, if (index % 2 == 1) Colors.Zinc100_50 else Color.Transparent) TransactionListItem(transaction, if (index % 2 == 1) Colors.Zinc100_50 else Color.Transparent)

View File

@ -0,0 +1,97 @@
package net.codinux.banking.ui.dialogs
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import net.codinux.banking.ui.forms.OutlinedTextField
import net.codinux.banking.ui.forms.PasswordTextField
import net.codinux.banking.ui.forms.RoundedCornersCard
import net.codinux.banking.ui.service.Colors
@Composable
fun AddAccountDialog(
onDismiss: () -> Unit,
) {
var bankCode by remember { mutableStateOf("") }
var loginName by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
val isRequiredDataEntered by remember(bankCode, loginName, password) {
derivedStateOf { bankCode.length == 8 && loginName.length > 3 && password.length > 3 }
}
val coroutineScope = rememberCoroutineScope()
Dialog(onDismissRequest = onDismiss) {
RoundedCornersCard {
Column(Modifier.background(Color.White).padding(8.dp)) {
Row(Modifier.fillMaxWidth()) {
Text(
"Bank Konto hinzufügen",
color = Color.Black,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(top = 8.dp, bottom = 16.dp).weight(1f)
)
TextButton(onDismiss, colors = ButtonDefaults.buttonColors(contentColor = Colors.Zinc700, backgroundColor = Color.Transparent)) {
Icon(Icons.Filled.Close, contentDescription = "Close dialog", Modifier.size(32.dp))
}
}
OutlinedTextField(
value = bankCode,
onValueChange = { bankCode = it },
label = { Text("Bank (Suche mit Name, Bankleitzahl oder Ort)") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(24.dp))
Text("Online-Banking Zugangsdaten", color = Colors.CodinuxSecondaryColor)
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = loginName,
onValueChange = { loginName = it },
label = { Text("Login Name") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
PasswordTextField(password) { password = it }
Spacer(modifier = Modifier.height(16.dp))
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
TextButton(onClick = onDismiss) {
Text("Abbrechen")
}
Spacer(Modifier.width(8.dp))
TextButton(
onClick = onDismiss,
enabled = isRequiredDataEntered
) {
Text("Hinzufügen")
}
}
}
}
}
}

View File

@ -0,0 +1,67 @@
package net.codinux.banking.ui.forms
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
import net.codinux.banking.ui.service.Colors
@Composable
fun OutlinedTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small,
// colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors() // TODO: merge
) {
val textFieldColors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Colors.CodinuxSecondaryColor,
focusedLabelColor = Colors.CodinuxSecondaryColor // does not work
)
androidx.compose.material.OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
isError = isError,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
minLines = minLines,
interactionSource = interactionSource,
shape = shape,
colors = textFieldColors
)
}

View File

@ -0,0 +1,46 @@
package net.codinux.banking.ui.forms
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import bankmeister.composeapp.generated.resources.Res
import bankmeister.composeapp.generated.resources.visibility
import bankmeister.composeapp.generated.resources.visibility_off
import org.jetbrains.compose.resources.imageResource
@Composable
fun PasswordTextField(password: String = "", label: String = "Passwort", onChange: (String) -> Unit) {
var passwordVisible by remember { mutableStateOf(false) }
OutlinedTextField(
value = password,
onValueChange = { onChange(it) },
label = { Text(label) },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
val visibilityIcon = if (passwordVisible) {
Res.drawable.visibility_off
} else {
Res.drawable.visibility
}
Icon(
bitmap = imageResource(visibilityIcon),
contentDescription = if (passwordVisible) "Hide password" else "Show password",
modifier = Modifier.size(24.dp).clickable { passwordVisible = !passwordVisible }
)
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
}

View File

@ -0,0 +1,16 @@
package net.codinux.banking.ui.forms
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun RoundedCornersCard(cornerSize: Dp = 12.dp, shadowElevation: Dp = 2.dp, content: @Composable () -> Unit) {
Card(
shape = RoundedCornerShape(cornerSize), // Rounded corners
elevation = shadowElevation, // Shadow elevation
content = content
)
}

View File

@ -4,6 +4,11 @@ import androidx.compose.ui.graphics.Color
object Colors { object Colors {
val CodinuxPrimaryColor = Color(30, 54, 78)
val CodinuxSecondaryColor = Color(251, 187, 33)
val Zinc100 = Color(244, 244, 245) val Zinc100 = Color(244, 244, 245)
val Zinc100_50 = Zinc100.copy(alpha = 0.5f) val Zinc100_50 = Zinc100.copy(alpha = 0.5f)