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())
|
setTransaction(bankService.getTransactions())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MaterialTheme(colors = colors, typography = typography) {
|
MaterialTheme(colors = colors, typography = typography) {
|
||||||
Box {
|
Box {
|
||||||
Column(
|
Column(
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package net.codinux.banking.ui.dialogs
|
package net.codinux.banking.ui.dialogs
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
@ -25,7 +24,7 @@ private val bankingService = DI.bankingService
|
||||||
fun AddAccountDialog(
|
fun AddAccountDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var bankCode by remember { mutableStateOf("") }
|
var selectedBank by remember { mutableStateOf<BankInfo?>(null) }
|
||||||
var loginName by remember { mutableStateOf("") }
|
var loginName by remember { mutableStateOf("") }
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
@ -54,14 +53,9 @@ fun AddAccountDialog(
|
||||||
AutocompleteTextField(
|
AutocompleteTextField(
|
||||||
onValueChange = { selectedBank = it },
|
onValueChange = { selectedBank = it },
|
||||||
label = { Text("Bank (Suche mit Name, Bankleitzahl oder Ort)") },
|
label = { Text("Bank (Suche mit Name, Bankleitzahl oder Ort)") },
|
||||||
|
getItemTitle = { bank -> bank.name },
|
||||||
fetchSuggestions = { query -> bankingService.findBanks(query) }
|
fetchSuggestions = { query -> bankingService.findBanks(query) }
|
||||||
) { bank ->
|
) { bank ->
|
||||||
Column(
|
|
||||||
Modifier.fillMaxWidth().clickable {
|
|
||||||
selectedItem = bank
|
|
||||||
}
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text(bank.name)
|
Text(bank.name)
|
||||||
|
|
||||||
Row(Modifier.fillMaxWidth().padding(top = 8.dp)) {
|
Row(Modifier.fillMaxWidth().padding(top = 8.dp)) {
|
||||||
|
@ -70,7 +64,6 @@ fun AddAccountDialog(
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,48 @@
|
||||||
package net.codinux.banking.ui.forms
|
package net.codinux.banking.ui.forms
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.CircularProgressIndicator
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
import androidx.compose.material.DropdownMenu
|
import androidx.compose.material.Divider
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
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.geometry.Size
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
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.dp
|
||||||
import androidx.compose.ui.unit.toSize
|
import androidx.compose.ui.unit.toSize
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import net.codinux.banking.ui.service.Colors
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> AutocompleteTextField(
|
fun <T> AutocompleteTextField(
|
||||||
onValueChange: (T) -> Unit,
|
onValueChange: (T?) -> Unit,
|
||||||
modifier: Modifier = Modifier.fillMaxWidth(),
|
modifier: Modifier = Modifier.fillMaxWidth(),
|
||||||
label: @Composable () -> Unit = { Text("Search") },
|
label: @Composable () -> Unit = { Text("Search") },
|
||||||
|
showDividersBetweenItems: Boolean = true,
|
||||||
|
getItemTitle: ((T) -> String)? = null,
|
||||||
fetchSuggestions: suspend (query: String) -> List<T> = { emptyList() },
|
fetchSuggestions: suspend (query: String) -> List<T> = { emptyList() },
|
||||||
suggestionContent: @Composable (T) -> Unit
|
suggestionContent: @Composable (T) -> Unit
|
||||||
) {
|
) {
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
var selectedItem by remember { mutableStateOf<T?>(null) }
|
|
||||||
var isLoading by remember { mutableStateOf(false) }
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
var suggestions by remember { mutableStateOf<List<T>>(emptyList()) }
|
var suggestions by remember { mutableStateOf<List<T>>(emptyList()) }
|
||||||
var textFieldSize by remember { mutableStateOf(Size.Zero) }
|
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()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
@ -39,19 +52,25 @@ fun <T> AutocompleteTextField(
|
||||||
value = searchQuery,
|
value = searchQuery,
|
||||||
onValueChange = { query ->
|
onValueChange = { query ->
|
||||||
searchQuery = query
|
searchQuery = query
|
||||||
selectedItem = null
|
onValueChange(null)
|
||||||
if (query.length >= 2) {
|
|
||||||
|
if (query.length >= 1) {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
suggestions = fetchSuggestions(query)
|
suggestions = fetchSuggestions(query)
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
expanded = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
suggestions = emptyList()
|
suggestions = emptyList()
|
||||||
|
expanded = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label = label,
|
label = label,
|
||||||
modifier = modifier.onGloballyPositioned {
|
modifier = modifier
|
||||||
|
.focusRequester(textFieldFocus)
|
||||||
|
.onGloballyPositioned {
|
||||||
textFieldSize = it.size.toSize()
|
textFieldSize = it.size.toSize()
|
||||||
},
|
},
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
|
@ -67,7 +86,8 @@ fun <T> AutocompleteTextField(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
searchQuery = ""
|
searchQuery = ""
|
||||||
suggestions = emptyList()
|
suggestions = emptyList()
|
||||||
selectedItem = null
|
expanded = false
|
||||||
|
onValueChange(null)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -75,20 +95,42 @@ fun <T> AutocompleteTextField(
|
||||||
// isError = selectedItem == null && searchQuery.isNotEmpty()
|
// isError = selectedItem == null && searchQuery.isNotEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
DropdownMenu(
|
if (expanded) {
|
||||||
expanded = expanded,
|
RoundedCornersCard(
|
||||||
onDismissRequest = { suggestions = emptyList() },
|
Modifier.width(textFieldSize.width.dp)
|
||||||
modifier = with(Modifier) {
|
|
||||||
width(textFieldSize.width.dp)
|
|
||||||
.heightIn(max = 400.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)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
suggestions.forEach { item ->
|
|
||||||
Column(Modifier.fillMaxWidth()) {
|
|
||||||
suggestionContent(item)
|
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.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Card
|
import androidx.compose.material.Card
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@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(
|
Card(
|
||||||
|
modifier,
|
||||||
shape = RoundedCornerShape(cornerSize), // Rounded corners
|
shape = RoundedCornerShape(cornerSize), // Rounded corners
|
||||||
elevation = shadowElevation, // Shadow elevation
|
elevation = shadowElevation, // Shadow elevation
|
||||||
content = content
|
content = content
|
||||||
|
|
Loading…
Reference in New Issue