简体   繁体   中英

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.

You can hoist the selected option state like this,

val (optionSelected, setOptionSelected)= remember {
    mutableStateOf("")
} 

PlantExposedSelect(
    options = options,
    optionSelected = optionSelected,
    label = "Label",
    onOptionSelected = {
        Log.e("eventValue", it)
        setOptionSelected(it)
        // viewModel.onEvent(AddEditPlantEvent.EnteredLight(it))
    },
    onFocusChange = {
        // viewModel.onEvent(AddEditPlantEvent.ChangedLightFocus(it))
    },
)

And replace selectedOption in PlantExposedSelect with optionSelected .


Complete code for reference

@Composable
fun PlantExposedSelectSample() {
    val options = listOf("Option 1", "Option 2", "Option 3", "Option 4")
    val (optionSelected, setOptionSelected)= remember {
        mutableStateOf("")
    }
    PlantExposedSelect(
        options = options,
        optionSelected = optionSelected,
        label = "Label",
        onOptionSelected = {
            Log.e("eventValue", it)
            setOptionSelected(it)
            // viewModel.onEvent(AddEditPlantEvent.EnteredLight(it))
        },
        onFocusChange = {
            // viewModel.onEvent(AddEditPlantEvent.ChangedLightFocus(it))
        },
    )
}

@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) }
    ExposedDropdownMenuBox(
        expanded = expanded,
        onExpandedChange = {
            expanded = !expanded
        }

    ) {
        TextField(
            readOnly = true,
            value = optionSelected,
            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 = {
                        onOptionSelected(selectOption)
                        expanded = false
                        Log.e("selectEdoption", selectOption)
                    }
                ) {
                    Text(text = selectOption)
                }
            }
        }
    }
}

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)
        }
      }
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM