4

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)
        )
    }
}


z.g.y
  • 5,512
  • 4
  • 10
  • 36
AdamPI
  • 53
  • 6

2 Answers2

3

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) }
z.g.y
  • 5,512
  • 4
  • 10
  • 36
1

Have you tried doing the following instead?

listOfWords.forEachIndexed { index, word ->
    ... // rest of code
    {
        listOfWords.removeAt(index)
    }
Arthur Kasparian
  • 486
  • 2
  • 12
  • your answer is useful because forEachIndexed is better for this, but is still doesn't work how I want. The problem is the list as a composable view doesn't refresh in the proper way. The very list contains correct values but it seems like compose removes always the last element (TextField) no matter what the index is. – AdamPI Oct 31 '22 at 18:47
  • I guess I did not see the issue correctly at first, someone else did though, still glad I was able to help somehow! – Arthur Kasparian Nov 02 '22 at 19:36