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.
CodePudding user response:
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)
}
}
}
}
}
CodePudding user response:
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)
}
}
}
}
}