Jetpack Compose: mutableStateOf doesn't update with flow

Issue

I have a ViewModel which uses Flow to get a Note object from my Room database:

var uiState by mutableStateOf(NoteUiState())
        private set


    private fun getNoteById(argument: Int) {
        viewModelScope.launch {
            try {
                repository.getNoteById(argument).collect { note ->
                    uiState = NoteUiState(note = note)
                }
            } catch (e: Exception) {
                uiState = NoteUiState(error = true)
            }
        }
    }

Note class:

@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    @ColumnInfo(name = "title") val title: String = "",
    @ColumnInfo(name = "text") val text: String = "",
) {
    override fun toString() = title
}

This approach works fine, until I try to make a mutable strings with the values of the Note object as their default so I can update 2 TextField composables:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun Note(
    note: DataNote,
    isNewNote: Boolean,
    createNote: (DataNote) -> Unit,
    updateNote: (DataNote) -> Unit,
    back: () -> Unit,
    trued: String,
) {

    var title by remember { mutableStateOf(note.title) }
    var content by remember { mutableStateOf(note.text) }
    TextField(
        value = title,
        onValueChange = { title = it },
        modifier = Modifier
            .fillMaxWidth(),
        placeholder = { Text(text = "Title") },
        colors = TextFieldDefaults.textFieldColors(
            containerColor = Color.Transparent
        )
    )
    TextField(
        value = content,
        onValueChange = { content = it },
        modifier = Modifier
            .fillMaxWidth(),
        placeholder = { Text(text = "Content") },
        colors = TextFieldDefaults.textFieldColors(
            containerColor = Color.Transparent
        )
    )
}

For some reason the first time the Note object is called it’s null, so I want a way to update the title and content variables.

The Note object itself updates without issue, however the title and content variables never change from the initial value. How can I update the title and content variables while also making them work for the textfield?

Solution

I found out how to make the Textfield work while also getting the inital value from the object. The issue was that the Note object was called as null on the first call, so the mutableStateFlow didnt get the initial values.

First, I had to pass the actual state as a MutableStateFlow to my composable:

@Composable
private fun Note(
    state: MutableStateFlow<NoteUiState>,
    createNote: (DataNote) -> Unit,
    updateNote: (DataNote) -> Unit,
    back: () -> Unit
) {
...

Next, I just had to get the Note object by calling collectAsState():

val currentNote = state.collectAsState().value.note

Finally, all that was needed was to pass the currentNote object text and title in the value of the Textfield, and on onValueChange to update the state object itself via a copy:

This is the complete solution:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun Note(
    state: MutableStateFlow<NoteUiState>,
    createNote: (DataNote) -> Unit,
    updateNote: (DataNote) -> Unit,
    back: () -> Unit
) {
    val currentNote = state.collectAsState().value.note
    Column(Modifier.fillMaxSize()) {
        TextField(
            value = currentNote.title,
            onValueChange = {
                state.value = state.value.copy(note = currentNote.copy(title = it))
                            },
            modifier = Modifier
                .fillMaxWidth(),
            placeholder = { Text(text = "Title") },
            colors = TextFieldDefaults.textFieldColors(
                containerColor = Color.Transparent
            )
        )
    }
}

I’m not sure is this is a clean solution, but is the only way it worked for me, thanks for the feedback, opinions on this approach are always welcomed.

Answered By – Baguingi

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published