From 13d922da873302d3fa1258ae5851b48880684a94 Mon Sep 17 00:00:00 2001 From: dankito Date: Mon, 26 Aug 2024 01:26:45 +0200 Subject: [PATCH] Fixed AutocompleteTextField, DropdownMenu just didn't work and always took focus from TextField --- .../kotlin/net/codinux/banking/ui/App.kt | 1 + .../banking/ui/dialogs/AddAccountDialog.kt | 19 ++--- .../banking/ui/forms/AutocompleteTextField.kt | 84 ++++++++++++++----- .../banking/ui/forms/RoundedCornersCard.kt | 4 +- 4 files changed, 73 insertions(+), 35 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt index 2ca2c3c..f072e1a 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/App.kt @@ -41,6 +41,7 @@ fun App() { setTransaction(bankService.getTransactions()) } + MaterialTheme(colors = colors, typography = typography) { Box { Column( diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/AddAccountDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/AddAccountDialog.kt index c583204..bf16470 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/AddAccountDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/AddAccountDialog.kt @@ -1,7 +1,6 @@ package net.codinux.banking.ui.dialogs import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons @@ -25,7 +24,7 @@ private val bankingService = DI.bankingService fun AddAccountDialog( onDismiss: () -> Unit, ) { - var bankCode by remember { mutableStateOf("") } + var selectedBank by remember { mutableStateOf(null) } var loginName by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } @@ -54,21 +53,15 @@ fun AddAccountDialog( AutocompleteTextField( onValueChange = { selectedBank = it }, label = { Text("Bank (Suche mit Name, Bankleitzahl oder Ort)") }, + getItemTitle = { bank -> bank.name }, fetchSuggestions = { query -> bankingService.findBanks(query) } ) { bank -> - Column( - Modifier.fillMaxWidth().clickable { - selectedItem = bank - } - .padding(8.dp) - ) { - Text(bank.name) + Text(bank.name) - Row(Modifier.fillMaxWidth().padding(top = 8.dp)) { - Text(bank.bankCode) + Row(Modifier.fillMaxWidth().padding(top = 8.dp)) { + Text(bank.bankCode) - Text("${bank.postalCode} ${bank.city}", Modifier.weight(1f).padding(start = 8.dp), color = Color.Gray) - } + Text("${bank.postalCode} ${bank.city}", Modifier.weight(1f).padding(start = 8.dp), color = Color.Gray) } } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt index dd7fb2a..431f6d1 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt @@ -1,35 +1,48 @@ package net.codinux.banking.ui.forms +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.verticalScroll import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.DropdownMenu +import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close +import androidx.compose.runtime.Composable import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.Size import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize import kotlinx.coroutines.launch +import net.codinux.banking.ui.service.Colors @Composable fun AutocompleteTextField( - onValueChange: (T) -> Unit, + onValueChange: (T?) -> Unit, modifier: Modifier = Modifier.fillMaxWidth(), label: @Composable () -> Unit = { Text("Search") }, + showDividersBetweenItems: Boolean = true, + getItemTitle: ((T) -> String)? = null, fetchSuggestions: suspend (query: String) -> List = { emptyList() }, suggestionContent: @Composable (T) -> Unit ) { var searchQuery by remember { mutableStateOf("") } - var selectedItem by remember { mutableStateOf(null) } var isLoading by remember { mutableStateOf(false) } var suggestions by remember { mutableStateOf>(emptyList()) } var textFieldSize by remember { mutableStateOf(Size.Zero) } - val expanded by remember(suggestions) { derivedStateOf { suggestions.isNotEmpty() } } + var expanded by remember { mutableStateOf(false) } + + val textFieldFocus = remember { FocusRequester() } + val focusManager = LocalFocusManager.current val coroutineScope = rememberCoroutineScope() @@ -39,21 +52,27 @@ fun AutocompleteTextField( value = searchQuery, onValueChange = { query -> searchQuery = query - selectedItem = null - if (query.length >= 2) { + onValueChange(null) + + if (query.length >= 1) { isLoading = true + coroutineScope.launch { suggestions = fetchSuggestions(query) isLoading = false + expanded = true } } else { suggestions = emptyList() + expanded = false } }, label = label, - modifier = modifier.onGloballyPositioned { - textFieldSize = it.size.toSize() - }, + modifier = modifier + .focusRequester(textFieldFocus) + .onGloballyPositioned { + textFieldSize = it.size.toSize() + }, trailingIcon = { if (isLoading) { CircularProgressIndicator( @@ -67,7 +86,8 @@ fun AutocompleteTextField( modifier = Modifier.clickable { searchQuery = "" suggestions = emptyList() - selectedItem = null + expanded = false + onValueChange(null) } ) } @@ -75,20 +95,42 @@ fun AutocompleteTextField( // isError = selectedItem == null && searchQuery.isNotEmpty() ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { suggestions = emptyList() }, - modifier = with(Modifier) { - width(textFieldSize.width.dp) - .heightIn(max = 400.dp) - } - ) { - suggestions.forEach { item -> - Column(Modifier.fillMaxWidth()) { - suggestionContent(item) + if (expanded) { + RoundedCornersCard( + Modifier.width(textFieldSize.width.dp) + .heightIn(max = 400.dp) + .padding(top = with(LocalDensity.current) { (textFieldSize.height + 2).toDp() }) + , + cornerSize = 4.dp, + shadowElevation = 4.dp + ) { + Column(Modifier.verticalScroll(ScrollState(0), enabled = true)) { + suggestions.forEachIndexed { index, item -> + Column( + Modifier.fillMaxWidth().padding(8.dp).clickable { + onValueChange(item) + getItemTitle?.let { + searchQuery = it.invoke(item) + } + expanded = false + focusManager.moveFocus(FocusDirection.Down) + } + ) { + suggestionContent(item) + } + + if (showDividersBetweenItems && index < suggestions.size - 1) { + Divider(color = Colors.Zinc200, thickness = 1.dp) + } + } } } } } + + LaunchedEffect(textFieldFocus) { + textFieldFocus.requestFocus() + } + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/RoundedCornersCard.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/RoundedCornersCard.kt index c9bcf42..ce9507f 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/RoundedCornersCard.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/RoundedCornersCard.kt @@ -3,12 +3,14 @@ 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.Modifier 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) { +fun RoundedCornersCard(modifier: Modifier = Modifier, cornerSize: Dp = 12.dp, shadowElevation: Dp = 2.dp, content: @Composable () -> Unit) { Card( + modifier, shape = RoundedCornerShape(cornerSize), // Rounded corners elevation = shadowElevation, // Shadow elevation content = content