Search code examples
kotlinandroid-jetpack-composecompose-desktop

Compose onValueChange behaviour isn't consistent


I'm making a sudoku game and solver. For my user interface I used LazyVerticalGrid to create a 9x9 grid. I successfully made it so when you click on a cell it will only accept digits [1-9] via an OutLinedTextField. I then added a conditional that only empty cells would have the text field applied. That worked and only those cells could be altered but when I do that the logic that only accepts digits doesn't work and the program crashes. If I comment out the conditional statement and the OutLinedTextField is applied to all cells it works again. I get the following error.Error

Also if I add conditionals for backgroundColor or Content Color the same thing happens and the program crashes if a non digit is pressed. I'm not sure why the conditionals affect the onValueChange logic. Why is this and how do I fix it?

fun displayPuzzle(answer: Array<Array<IntArray>>) {
    var list: SnapshotStateList<String> = mutableStateListOf()
    for (x in answer[0]) list.addAll(x.map { it.toString() })

    var columnHeighty by remember { mutableStateOf(0F) }
    var columnWidthx by remember { mutableStateOf(0f) }
    var pad = 20.dp

    LazyVerticalGrid(

        columns = GridCells.Fixed(9),
        contentPadding = PaddingValues(
            start = pad,
            top = pad,
            end = pad,
            bottom = pad
        )

    ) {
        items(list.size) { index ->
            Card(
                shape = RectangleShape,
                backgroundColor = Color.Red,
                modifier = Modifier
                    .requiredWidth(83.dp)
                    .fillMaxWidth()
                    .fillMaxHeight()
                    .onGloballyPositioned { coordinates ->
                        columnWidthx = coordinates.size.width.toFloat()
                        columnHeighty = coordinates.size.height.toFloat()
                    },
                //backgroundColor = if (list[index].toInt() == 0) Color.Yellow else Color.White ,
                //contentColor = if (list[index].toInt() == 0) Color.Blue else Color.Black ,
                border = BorderStroke(width = 1.dp, color = Color.Black)
            ) {
                Text(
                    text = list[index],
                    fontWeight = FontWeight.Bold,
                    fontSize = 30.sp,
                    color = Color(0xFF000000),
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .padding(23.dp)
                        .clickable { }
                )
            }

            // When the if statement is included the program crashes on a non digit entry
            //if (list[index].toInt() == 0) {
                val pattern = remember { Regex("[1-9]") }
                var value by remember { mutableStateOf("") }
                OutlinedTextField(
                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                    colors = TextFieldDefaults.outlinedTextFieldColors(cursorColor = Color.Transparent),
                    textStyle = TextStyle(color = Color.Red),
                    modifier = Modifier
                        .fillMaxHeight()
                        .padding(vertical = 10.dp, horizontal = 10.dp),
                    value = value,
                    onValueChange = { if (it.isEmpty() || (it.matches(pattern) && (it.length == 1)))
                        value = it
                        list[index] = value}
                )
            //}
        }
    }

Solution

  • Your game crashed because you trying to convert for example 'a' to Int value and runtime throws NumberFormatException. You need to use:

    if (list[index].toIntOrNull() == null)
    

    This condition will be triggered if a non-decimical number is obtained from your SnapshotStateList

    Explanation: toIntOrNull() returns Int from String (example: "4".toIntOrNull() - returns 4) otherwise it returns null