I have a statistics screen that uses drop down menus to add filters. One filter for example has all of the game types with check boxes next to each game. It's initially populated with all of the boxes checked so all games are included in the stats. As the user unchecks games the stats update without the games that were unchecked. Everything is working as I want except that the statistics won't show the updated values until I close the dropdown menu. The checkboxes are updating as they are checked. I used debug to see that the statistics are updated as the boxes are checked or unchecked.
val selectedGame = remember { SnapshotStateList<String>() }
ExposedDropdownMenuBox(
expanded = expandedGame,
onExpandedChange = { onExpandGameChange(it) },
) {
ShowOutlinedTextField(
readOnly = true,
value = stringResource(id = R.string.game_filter),
modifier = Modifier
.padding(10.dp)
.menuAnchor(type = MenuAnchorType.PrimaryNotEditable,
enabled = true)
)
ExposedDropdownMenu(
expanded = expandedGame,
onDismissRequest = { onExpandGameChange(false) }
) {
gameNames.forEach { game ->
DropdownMenuItem(
onClick = { onExpandGameChange(false) },
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked =
selectedGame.contains(game.gameName),
onCheckedChange = { isChecked ->
if (isChecked) {
selectedGame.add(game.gameName)
} else {
selectedGame.remove(game.gameName)
}
setSelectedGames(selectedGame.toList())
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = game.gameName)
}
}
)
}
}
}
I tried adding the following code but it didn't work.
var forceRecomposition by remember { mutableStateOf(0) }
Checkbox(
checked = selectedLocations.contains(casino.name),
onCheckedChange = { isChecked ->
if (isChecked) {
selectedLocations.add(casino.name)
} else {
selectedLocations.remove(casino.name)
}
setSelectedCasinos(selectedLocations.toList())
forceRecomposition++
}
)
You should lift the state that is common to the ExposedDropdownMenu
Composable and your statistics Composable to the common parent Composable. Then, when the user clicks a Checkbox
,
Please have a look at the following sample code:
@Composable
fun MyComposableScreen() {
val items = remember { arrayOf("Item A", "Item B", "Item C", "Item D") }
val selectedItems = remember { mutableStateListOf(*items) }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceAround,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyDropdownMenu(
allItems = items.toList(),
selectedItems = selectedItems,
addItem = { newItem ->
selectedItems.add(newItem)
},
removeItem = { removedItem ->
selectedItems.remove(removedItem)
}
)
MyStatistics(selectedItems)
}
}
The MyDropdownMenu
Composable looks as follows:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyDropdownMenu(
allItems: List<String>,
selectedItems: List<String>,
addItem: (String) -> Unit,
removeItem: (String) -> Unit
) {
var isExpanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = isExpanded,
onExpandedChange = { isExpanded = it },
) {
TextField(
modifier = Modifier.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable),
state = TextFieldState(selectedItems.joinToString(",")),
readOnly = true,
lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("Label") },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = isExpanded) },
colors = ExposedDropdownMenuDefaults.textFieldColors(),
)
ExposedDropdownMenu(
expanded = isExpanded,
onDismissRequest = { isExpanded = false }
) {
allItems.forEach { itemName: String ->
DropdownMenuItem(
onClick = { /** do nothing or call addItem() / removeItem() **/ },
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = selectedItems.contains(itemName),
onCheckedChange = { isChecked ->
if (isChecked) {
addItem(itemName)
} else {
removeItem(itemName)
}
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = itemName)
}
}
)
}
}
}
}
Finally, the MyStatistics
Composable could look like this:
@Composable
fun MyStatistics(selectedItems: List<String>) {
Text(
"SELECTED ITEMS: \n${selectedItems.joinToString(",")}",
fontSize = 24.sp
)
}
Note that is good practice to use mutableStateListOf
instead of directly remember
ing a SnapshotStateList
.
Output: