Fixed AutocompleteTextField, DropdownMenu just didn't work and always took focus from TextField

This commit is contained in:
dankito 2024-08-26 01:26:45 +02:00
parent 972af95a11
commit 13d922da87
4 changed files with 73 additions and 35 deletions

View File

@ -41,6 +41,7 @@ fun App() {
setTransaction(bankService.getTransactions())
}
MaterialTheme(colors = colors, typography = typography) {
Box {
Column(

View File

@ -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<BankInfo?>(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)
}
}

View File

@ -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 <T> 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<T> = { emptyList() },
suggestionContent: @Composable (T) -> Unit
) {
var searchQuery by remember { mutableStateOf("") }
var selectedItem by remember { mutableStateOf<T?>(null) }
var isLoading by remember { mutableStateOf(false) }
var suggestions by remember { mutableStateOf<List<T>>(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 <T> 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 <T> AutocompleteTextField(
modifier = Modifier.clickable {
searchQuery = ""
suggestions = emptyList()
selectedItem = null
expanded = false
onValueChange(null)
}
)
}
@ -75,20 +95,42 @@ fun <T> 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()
}
}

View File

@ -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