Search code examples
androidkotlinandroid-jetpack-composeandroid-compose-textfieldandroid-compose-exposeddropdown

onValueChange of textfield is not triggered when selecting an option from exposed dropdownMenu composable, jetpack compose


I am trying to implement an exposed dropdown composable which I can use in multiple parts of my android jetpack compose app. Whenever I select an item from the dropdownMenu the selectedOption is set in the composable and assigned to the textfield value displaying the correct item. However the onValueChange event of the Textfield displaying the result is not fired. This causes the state not being updated in the viewmodel layer of my app. Following my code of my composable.

// ExposedDropdownComposable.kt    
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PlantExposedSelect(
  options: List<String>,
  optionSelected: String,
  label: String,
  onOptionSelected: (String) -> Unit,
  onFocusChange: (FocusState) -> Unit,
) {
  var expanded by remember { mutableStateOf(false) }
  var selectedOption by remember { mutableStateOf(optionSelected) }
  ExposedDropdownMenuBox(
    expanded = expanded,
    onExpandedChange = {
      expanded = !expanded
    }

  ) {
    TextField(
      readOnly = true,
        value = selectedOption,
      onValueChange = onOptionSelected
      label = { Text(label) },
      trailingIcon = {
        ExposedDropdownMenuDefaults.TrailingIcon(
          expanded = expanded
        )
      },
      colors = ExposedDropdownMenuDefaults.textFieldColors(),
      modifier = Modifier
        .fillMaxWidth()
        .onFocusChanged {
          onFocusChange(it)
        },
    )
    ExposedDropdownMenu(
      expanded = expanded,
      onDismissRequest = {
        expanded = false
      }
    ) {
      options.forEach { selectOption ->
        DropdownMenuItem(
          onClick = {
            selectedOption = selectOption
            expanded = false
            Log.e("selectEdoption", selectedOption)
          }
        ) {
          Text(text = selectOption)
        }
      }
    }
  }
}

This is my code where I use the composable in my AddPlantsScreen

PlantExposedSelect(
  options = options,
  optionSelected = lightState.text,
  label = lightState.hint,
  onOptionSelected = {
    Log.e("eventValue", it)
    viewModel.onEvent(AddEditPlantEvent.EnteredLight(it))
  },
  onFocusChange = {
    viewModel.onEvent(AddEditPlantEvent.ChangedLightFocus(it))
  },
)

How do I make the onClick event of the dropdownItem, trigger the onValueChange event of the Textfield dislaying the selectedOption.


Solution

  • You can simply call the onOptionSelected in the onClick parameter of the DropdownMenuItem instead of using the onValueChange.

    Something like:

    @Composable
    fun PlantExposedSelect(
      //...
      onOptionSelected: (String) -> Unit,
    ) {
      var expanded by remember { mutableStateOf(false) }
      var selectedOption by remember { mutableStateOf(optionSelected) }
    
      ExposedDropdownMenuBox(
        //....
      ) {
        TextField(
          value = selectedOption,
          onValueChange = {},   //remove the function
          //...
        )
        ExposedDropdownMenu(
          /** ... **/
          }
        ) {
          options.forEach { selectOption ->
            DropdownMenuItem(
              onClick = {
                selectedOption = selectOption
                expanded = false
                onOptionSelected         //update your viewmodel here
              }
            ) {
              Text(text = selectOption)
            }
          }
        }
      }
    }