Home > Blockchain >  Jetpack Compose: mutableStateOf doesn't update with flow
Jetpack Compose: mutableStateOf doesn't update with flow

Time:08-28

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?

CodePudding user response:

 var title by remember { mutableStateOf(note.title) }
    var content by remember { mutableStateOf(note.text) }

remember block executes only on 1st composition and then value will remembered until decomposition or u need to change it externally through '=' assignment operator,

.

instated of this

TextField(
        value = title)

write this way

 TextField(
            value = note.title)

enter image description here

CodePudding user response:

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.

  • Related