Home > OS >  What is the proper way to update list state after inserting an item via a repository in Compose?
What is the proper way to update list state after inserting an item via a repository in Compose?

Time:10-17

I have a Room app which displays a list of presets, and the user has the option of adding them by pressing a 'New Quick Preset' button:

Code for inserting new item into Room database (in ViewModel):

fun insertQuickPreset(quickPreset: QuickPreset) {
    viewModelScope.launch {
        repository.insertQuickPreset(quickPreset)
    }
}

State:

data class NewScreenState(
    @StringRes val nameError: Int? = null,
    @StringRes val widthError: Int? = null,
    @StringRes val heightError: Int? = null,
    val name: String = "",
    val width: String = "",
    val height: String = "",
    val quickPresets: List<QuickPreset>
)

Repo:

suspend fun insertQuickPreset(quickPreset: QuickPreset) {
    return withContext(Dispatchers.IO) {
        dao.insert(quickPreset)
    }
}

Composable:

Button(
    modifier = Modifier.align(Alignment.End),
    onClick = {
        viewModel.insertQuickPreset(QuickPreset(55,52))
    }
) {
    Text(
        "New Quick Preset"
    )
}

The issue is, when the user taps 'New Quick Preset', the state doesn't get updated, so for the user to see the results they need to go back and then press the button to enter the screen again.

I took a course in Compose and they never demonstrated how to add items, only how to fetch them. I have the code for adding an item, although I have no idea how I would update the state from here to reflect the added values? I usually utilize the .copy method but this is only for when the database contents are first fetched.

Any help would be appreciated.

CodePudding user response:

Use SnapshotStateList.

val quickPresets: SnapShotStateList

and initializing it this way

val list = mutableStateListOf( your quick presets )

Any updates you do to a SnapshotStateList, such as

  • add new quickPreset
  • remove a quickPreset
  • update a quickPreset via .copy()

is guaranteed to perform a re-composition, assuming its being observed by a composable somewehere

Edit: Assuming you're using Room database, here is a snippet of my own implementation

Repository

override fun fetchPeople(): Flow<List<People>> =
        peopleDao.getPeople().map { entityList ->
            entityList.map { entity ->
                entity.toModel()
            }
    }

override suspend fun savePerson(PersonModel: PersonModel): Long {
        return peopleDao.insertPerson(PersonModel.toEntity())
}

override suspend fun deletePerson(PersonModel: PersonModel) {
        peopleDao.deletePerson(PersonModel.toEntity())
}

And I'm just observing it via ViewModel

private val _people = mutableStateListOf<Person>()
val people: List<Person> = _people

...
...

// no need for any 'get/fectch' call, changes being made
// to the repository (e.g add or delete) will be propagated back via `flow`
viewModelScope.launch {
      repository.fetchPeople()
        .collect {
             _people.addAll(it)
        }
}

For your scrolling issue, you can do something like this,

 val scrollState = rememberLazyListState()
 val coroutineScope = rememberCoroutineScope()
    
 // scrollToItem after a successful `re-composition`
 SideEffect {
     coroutineScope.launch {
        scrollState.scrollToItem(index)
     }
 }

Let the composable that contains your list finish what ever it needs to do, and do the scrollToItem after its successful re-composition

  • Related