JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.26k stars 1.18k forks source link

TextField doesn't update when the ExposedDropdownMenu is expanded #4077

Closed 456634 closed 10 months ago

456634 commented 10 months ago

When attempting to type into the text field while the dropdown menu is open, the onValueChange() is not triggered.

TextDropdownField(
        state = rememberDropdownMenuState(setOf("A","B","C"), excludeSelectedItem = true),
        readOnly = false,
        itemText = { it }
    )
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun<T> TextDropdownField(
    state: DropdownMenuState<T>,
    readOnly: Boolean = true,
    label: @Composable (() -> Unit)? = null,
    itemText: (T) -> String
) {
    ExposedDropdownMenuBox(
        expanded = state.expanded,
        onExpandedChange = state::onExpandedChange,
        content = state.boxContent(
            readOnly = readOnly,
            label = label,
            itemText = itemText
        )
    )
}

@OptIn(ExperimentalMaterial3Api::class)
private fun<T> DropdownMenuState<T>.boxContent(
    readOnly: Boolean,
    label: @Composable (() -> Unit)?,
    itemText: (T) -> String
): @Composable ExposedDropdownMenuBoxScope.() -> Unit = {
    TextField(
        value = textInput,
        placeholder = { Text(itemText(selectedOption)) },
        onValueChange = ::onValueChange,
        modifier = Modifier.menuAnchor(),
        readOnly = readOnly,
        label = label,
        trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },
        colors = ExposedDropdownMenuDefaults.textFieldColors(),
    )

    ExposedDropdownMenu(
        expanded = expanded,
        onDismissRequest = ::onDismissRequest,
        content = {
            val options = if(excludeSelectedItem) (options - selectedOption) else options

            options.forEach { option ->
                DropdownMenuItem(
                    onClick = { onClick(option) },
                    text = { Text(itemText(option)) },
                    contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding
                )
            }
        }
    )
}
@Composable
fun<T> rememberDropdownMenuState(
    options: Set<T>,
    init: T = options.first(),
    excludeSelectedItem: Boolean = false
) = remember { DropdownMenuState(options, excludeSelectedItem, init) }

class DropdownMenuState<T>(
    val options: Set<T>,
    val excludeSelectedItem: Boolean,
    init: T
) {
    var expanded by mutableStateOf(false)
        private set
    var selectedOption by mutableStateOf(init)
        private set
    var textInput by mutableStateOf(TextFieldValue())
        private set

    fun onClick(value: T) {
        selectedOption = value
        expanded = false
    }

    fun onValueChange(value: TextFieldValue) {
        textInput = value
        expanded = true
    }

    fun onExpandedChange(isExpanded: Boolean) {
        expanded = isExpanded
    }

    fun onDismissRequest() {
        expanded = false
    }
}

Affected platforms

Versions

dima-avdeev-jb commented 10 months ago

Made reproducible sample project here: https://github.com/dima-avdeev-jb/issue-4077-textfield-dropdown/tree/613dc083c5e624e1453d434cc0f8b9a29eb3dbd1

dima-avdeev-jb commented 10 months ago

This behavior is also reproduced on Android as well. TextField looses focus - that's why it happends

dima-avdeev-jb commented 10 months ago

I don't know exactly is it bug or not. Because behavior to change focus is desired. Can you please create an Issue with the same description in Android JetPack Compose Issue tracker https://issuetracker.google.com/issues/new?component=612128

And link this issue here. First of all Android team should determin how this should behave. Thanks!

dima-avdeev-jb commented 10 months ago

As a workaround pay attention to this project: https://github.com/MaheshaGubbi/mgv_auto_search_textfield/blob/master/app/src/main/java/com/mgvautosearchtextfield/MainActivity.kt

This sample don't loose focus of TextField while expanding

456634 commented 10 months ago

Hi Dima, thanks for your quick response. I would like to report it to Google, but currently, I don't have an account. I'm sorry.

I understand your point, and I agree that the focus should switch. However, I think there should be a way to regain focus. I have already tried this with FocusRequester(), but unfortunately without success.

m-sasha commented 10 months ago

You can avoid the dropdown menu from grabbing the focus by using DropdownMenu instead of ExposedDropdownMenu and passing it PopupProperties(focusable = false), e.g.:

    DropdownMenu(
        expanded = expanded,
        onDismissRequest = onDismissRequest,
        modifier = modifier.exposedDropdownSize(),
        properties = PopupProperties(focusable = false),
    ) {
        // content here
    }
dima-avdeev-jb commented 10 months ago

@456634 If @m-sasha describe a proper way for you. Can we close this Issue? I will close it - but feel free to message us with request to reopen

okushnikov commented 2 months ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.