Fixed AutocompleteTextField, DropdownMenu just didn't work and always took focus from TextField
This commit is contained in:
parent
972af95a11
commit
13d922da87
|
@ -41,6 +41,7 @@ fun App() {
|
|||
setTransaction(bankService.getTransactions())
|
||||
}
|
||||
|
||||
|
||||
MaterialTheme(colors = colors, typography = typography) {
|
||||
Box {
|
||||
Column(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue