Search code examples
androidkotlininputandroid-jetpack-composecompose-recomposition

jetpack compose removing elements from list of text fields


list of text fields.

I want my code to remove elements from list of text fields properly. Each element has an X button to remove it's text field. If I start removing elements from the bottom it works but it doesn't work for removing random elements I want to use forEachIndexed for displaing the list Please help me with solving this problem. I've been trying to do this for some time but every trial is unsuccessful.

This is a piece of code that I've managed to write but removing elements doesn't work properly

val listOfWords = mutableStateListOf<String>()


@Composable
fun Main() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState()),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {

        Text(
            text = "Words",
            modifier = Modifier.padding(0.dp, 0.dp, 0.dp, 4.dp),
            style = MaterialTheme.typography.h6
        )

            listOfWords.forEachIndexed { index, word ->
                Input(word, 30, "Word", 1,
                    {newWord ->
                        listOfWords[index] = newWord
                        Log.d("text ",word)
                    },
                    {
                        listOfWords.removeAt(index)
                    }
                )
            }

        IconButton(
            onClick = {
                listOfWords.add("")
            }
        ) {
            Icon(
                imageVector = Icons.Filled.Add,
                contentDescription = "Add"
            )
        }


    }
}

@Composable
fun Input(
    word: String,
    maxChar: Int,
    label: String,
    maxLines: Int,
    onEdit: (word: String) -> (Unit),
    onRemove: () -> (Unit)
) {
    var text by remember { mutableStateOf(word) }
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp, 0.dp, 8.dp, 0.dp)
    ) {
        OutlinedTextField(
            value = text,
            onValueChange = {
                if (it.length <= maxChar) text = it
                onEdit(text)
            },
            modifier = Modifier.fillMaxWidth(),
            label = { Text(label) },
            leadingIcon = {
                Icon(Icons.Default.Edit, null)
            },
            trailingIcon = {
                IconButton(onClick = {
                    onRemove()
                }) {
                    Icon(
                        imageVector = Icons.Default.Clear,
                        contentDescription = "Back"
                    )
                }
            },
            maxLines = maxLines
        )

        Text(
            text = "${text.length} / $maxChar",
            textAlign = TextAlign.End,
            style = MaterialTheme.typography.caption,
            modifier = Modifier
                .fillMaxWidth()
                .padding(end = 16.dp)
        )
    }
}



Solution

  • The problem is here.

    var text by remember { mutableStateOf(word) }
    

    Without supplying a key to Input's remember, compose will not be able refresh/update your remaining Input's states during Main's re-composition every time an Input is removed.

    You can use the word parameter as key for remember to re-calculate every composition pass (i.e when you add/remove or typed a value in the TextField), and your code should probably work as you expected.

    var text by remember(word) { mutableStateOf(word) }