For the below code, I'm trying to get the selectedColorsText
to update whenever a user clicks on a button. The button background should then turn black and the text inside it white once selected. If de-selected, the button should turn back to white and the text inside it black. However, neither the selectedColorsText
or the button colors are updating as expected. What am I doing wrong?
enum class ColorsEnum(val rawValue: String) {
red("Red"),
orange("Orange"),
yellow("Yellow"),
green("Green"),
blue("Blue")
}
@Composable
fun LazyGridView() {
var selectedColors by remember { mutableStateOf(arrayListOf<String>()) }
var selectedColorsText by remember { mutableStateOf("") }
Column (
verticalArrangement = Arrangement.spacedBy(10.dp)
)
{
Text("Selected Colors: $selectedColorsText")
LazyVerticalGrid(
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
items(ColorsEnum.values()) { color ->
Button(
onClick = {
if (selectedColors.contains(color.rawValue)) {
selectedColors.remove(color.rawValue)
selectedColorsText = selectedColors.joinToString(", ")
Log.d("selectedColors = ", "$selectedColors")
Log.d("selectedColorsText = ", "$selectedColorsText")
} else {
selectedColors.add(color.rawValue)
selectedColors.sortBy { colorName ->
ColorsEnum.values()
.find { it.rawValue == colorName }
?.let(ColorsEnum.values()::indexOf)
}
Log.d("selectedColors = ", "$selectedColors")
Log.d("selectedColorsText = ", "$selectedColorsText")
}
},
modifier = Modifier
.border(
width = 1.dp,
color = Color.Black,
shape = RoundedCornerShape(5.dp)
),
colors = ButtonDefaults.buttonColors(
backgroundColor =
if (isSystemInDarkTheme()) {
if (selectedColors.contains(color.rawValue)) {
Color.White
} else {
Color.Black
}
} else {
if (selectedColors.contains(color.rawValue)) {
Color.Black
} else {
Color.White
}
}
),
) {
Text(
text = color.rawValue,
style = TextStyle(
fontWeight = FontWeight.Bold,
color =
if (isSystemInDarkTheme()) {
if (selectedColors.contains(color.rawValue)) {
Color.Black
} else {
Color.White
}
} else {
if (selectedColors.contains(color.rawValue)) {
Color.White
} else {
Color.Black
}
}
),
)
}
}
}
}
}
The main problem in your code is that you are trying to change the state, not its contents. From your code I made a simple example. In addition, try to look at the UDF (Unidirectional Data Flow) approach, it will help a lot in understanding how to work with a compose
class MainActivity : ComponentActivity() {
// Create flow in ViewModel layer
private val _selectedColors = MutableStateFlow<List<ColorsEnum>>(listOf())
val selectedColors: StateFlow<List<ColorsEnum>> = _selectedColors
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestApplicationTheme {
// Subscribe on "selectedColors" flow in compose layer
val selectedList by selectedColors.collectAsStateWithLifecycle()
Grid(selectedList, ::processColor)
}
}
}
private fun processColor(color: ColorsEnum) {
// update selected colors in ViewModel layer
_selectedColors.update { selected ->
if (selected.contains(color)) {
selected - color
} else {
selected + color
}
}
}
}
enum class ColorsEnum(val rawValue: String) {
RED("Red"),
ORANGE("Orange"),
YELLOW("Yellow"),
GREEN("Green"),
BLUE("Blue")
}
@Composable
private fun Grid(selectedColors: List<ColorsEnum>, onClick: (ColorsEnum) -> Unit) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
items(ColorsEnum.values()) { color ->
GridItem(
color = color,
isChecked = selectedColors.contains(color),
// also look on command pattern for process different
// kinds callback and data from your UI
onClick = onClick
)
}
}
}
@Composable
private fun GridItem(
color: ColorsEnum,
isChecked: Boolean,
onClick: (ColorsEnum) -> Unit
) {
Button(
modifier = Modifier
.border(
width = 1.dp,
color = Color.Black,
shape = RoundedCornerShape(5.dp)
),
colors = ButtonDefaults.buttonColors(
backgroundColor =
if (isSystemInDarkTheme()) {
if (isChecked) {
Color.White
} else {
Color.Black
}
} else {
if (isChecked) {
Color.Black
} else {
Color.White
}
}
),
onClick = {
onClick.invoke(color)
},
) {
Text(
text = color.rawValue,
color = if (isSystemInDarkTheme()) {
if (isChecked) {
Color.Black
} else {
Color.White
}
} else {
if (isChecked) {
Color.White
} else {
Color.Black
}
}
)
}
}