Home >Java >UI not reassembled after deleting all items in jetpack using room database

UI not reassembled after deleting all items in jetpack using room database

WBOY
WBOYforward
2024-02-06 08:15:04554browse
Question content

I have a movie app built using jetpack compose. In it I am showing the favorite movies selected by the user. The favorites should exist in the UI perfectly, but when I delete an item from the UI, the UI does not immediately reorganize to reflect the change. what to do?

This is my user interface

@composable
fun favscreen(
    movieviewmodel: movieviewmodel
) {

    remember { mutablestateof(movieviewmodel.getfavmovies()) }
    val shouldshowdialog = remember { mutablestateof(false) }
    val uistates = movieviewmodel.favs.collectasstate()


    column {
        row {
            text(
                text = "fav movies",
                fontsize = 25.sp,
                fontfamily = fontfamily(font(r.font.nsb)),
                modifier = modifier.padding(20.dp).weight(1f)
            )

            iconbutton(onclick = {
                shouldshowdialog.value  = true
            }) {
                icon(icons.filled.delete, contentdescription = "")
            }
        }
        when(val currentstate = uistates.value){
            is movieviewmodel.uistates.loading -> {
                box(modifier = modifier.fillmaxsize(), contentalignment = alignment.center) {
                    circularprogressindicator()
                }
            }
            is movieviewmodel.uistates.favs -> {
                val data = currentstate.data
                if(data.isempty()){
                    box(modifier = modifier.fillmaxsize(), contentalignment = alignment.center) {
                        text(
                            text = "no fav movies",
                            fontsize = 25.sp,
                            fontfamily = fontfamily(font(r.font.nsb)),
                            modifier = modifier.padding(20.dp))
                    }
                }
                else {
                    lazycolumn(){
                        items(data){ fav ->
                            row(modifier = modifier
                                .padding(20.dp)
                                .fillmaxwidth()
                                .clip(roundedcornershape(16.dp))
                                .background(color = color.darkgray)) {
                                asyncimage(
                                    model = utils.image_url + fav.imageurl,
                                    contentdescription = "",
                                    contentscale = contentscale.crop,
                                    filterquality = filterquality.high,
                                    modifier = modifier
                                        .width(150.dp)
                                        .height(150.dp))

                                column(modifier = modifier.padding(20.dp)) {
                                    text(
                                        text = fav.title!!,
                                        fontsize = 20.sp,
                                        fontfamily = fontfamily(font(r.font.nsb)))
                                    text(
                                        text = fav.rating.tostring() + "/10 imdb",
                                        fontsize = 15.sp,
                                        fontfamily = fontfamily(font(r.font.nsb)))
                                    text(
                                        text = "movie id : #" + fav.movieid,
                                        fontsize = 15.sp,
                                        fontfamily = fontfamily(font(r.font.nsb)))
                                }
                            }
                        }
                    }
                }
            }
            is movieviewmodel.uistates.error -> {
                box(modifier = modifier.fillmaxsize(), contentalignment = alignment.center){
                    text(
                        text = "no fav movies",
                        fontsize = 25.sp,
                        fontfamily = fontfamily(font(r.font.nsb)),
                        modifier = modifier.padding(20.dp))
                }
            }
            else -> {}
        }
    }
    if (shouldshowdialog.value){
        alertdialog(
            ondismissrequest = { shouldshowdialog.value = false },
            confirmbutton = {
                textbutton(onclick = {
                    movieviewmodel.deletemovies()
                    shouldshowdialog.value = false
                }) {
                    text(text = "proceed", fontfamily = fontfamily(font(r.font.nsm)))
                }
            },
            dismissbutton = {
                textbutton(onclick = {
                    shouldshowdialog.value = false
                }) {
                    text(text = "cancel", fontfamily = fontfamily(font(r.font.nsm)))
                }
            },
            shape = roundedcornershape(16.dp),
            text = { text(text = "favs deletion",fontfamily = fontfamily(font(r.font.nsb)))},
            title = { text(text = "do you want to delete all fav movies ?", fontfamily = fontfamily(font(r.font.nsb)))}
        )
    }
}

This is my view model

@HiltViewModel
class MovieViewModel @Inject constructor(
    private val movieRepoImpl: MovieRepoImpl
) : ViewModel() {
    

    private val _favs : MutableStateFlow<UiStates> = MutableStateFlow(UiStates.INITIAL)
    val favs get() = _favs.asStateFlow()


    // MOVIE DAO
    fun getFavMovies() = viewModelScope.launch {
        try {
            _favs.value = UiStates.LOADING
            movieRepoImpl.getFavs().collectLatest {
                _favs.value = UiStates.FAVS(it)
            }
        } catch (ex : Exception){
            _favs.value = UiStates.ERROR(ex.localizedMessage!!)
        }

    }
    fun insertMovie(favModel: FavModel) = viewModelScope.launch {
        movieRepoImpl.insertFav(favModel)
    }
    fun deleteMovies() = viewModelScope.launch {
        movieRepoImpl.deleteAllMovies()
    }

     sealed class UiStates {
         object LOADING : UiStates()
         data class FAVS(val data : MutableList<FavModel>) : UiStates()
         data class ERROR(val error : String) : UiStates()
         object INITIAL : UiStates()
     }
}

Correct answer


The problem becomes very obvious when you look at the written usage of _favs: Only getfavmovies will update it.

While this sounds like "you should update your UI state in the delete function", I'd like to propose an alternative solution: Harness the power of room flows!

First define loadingstate in the viewmodel, which will be stored as a stream:

sealed class loadingstate {
  object loading : loadingstate()
  data class success : loadingstate()
  data class error(val error : string) : loadingstate()
  object initial : loadingstate()
}

private val loadingstate = mutablestateflow<loadingstate>(initial)

Then, change the getfavmovies() function to use this mechanism:

fun getfavmovies() {
    viewmodelscope.launch {
        try {
            _loadingstate.value = loadingstate.loading
            movierepoimpl.loadfavs() // a function which triggers e.g. a network call to actually load your movies
            _loadingstate.value = loadingstate.success
        } catch (ex: exception) {
            _loadingstate.value = loadingstate.error(ex.localizedmessage!!)
        }
    }
}

Finally, combine your status and data to make magic happen. This will automatically update once one of them changes :)

val favs: flow<uistates> = combine(
    movierepoimpl.getmovies(),
    loadingstate
) { movies, loadingstate ->
    when (loadingstate) {
        is loadingstate.loading -> uistates.loading
        is loadingstate.success -> uistates.success(movies)
        is loadingstate.error -> uistates.error(error)
        is loadingstate. initial -> uistates.initial
    }
}

Disclaimer: There may be spelling errors in this, I don't have a handy ide

Let's finish this answer by improving the code:

  1. Avoid using = with functions that return nothing (getfavmovies, insertmovie, deletemovies). Otherwise, you would expect e.g. getfavmovies to return a list of movies

  2. remember { mutablestateof(movieviewmodel.getfavmovies()) } doesn't really remember anything, does it? This is so annoying. I suggest moving it into the viewmodel: init { movieviewmodel.getfavmovies() }

  3. val uistates = movieviewmodel.favs.collectasstate(): The naming means this is multiple ui states, please use the singular; uistates The same goes for the class itself. In addition, if ".value" is used directly here, you can skip the redeclaration here: when(val currentstate = uistates.value)

  4. Your uistate contains mutablelist9fed9bb6bc0c66d7a13f33ab448de4c5. Using mutable values ​​for ui state is a bad idea, for example it may mess up your stateflow. Be sure to avoid this situation. Just use the plain old list :) https://www.php.cn/link/ff685590317f1330efc73f396ac92cd7

  5. Use collectasstatewithlifecycle() instead of collectasstate(). See https://medium.com/androiddevelopers/consuming-flows -safely in jetpack-compose-cde014d0d5a3

  6. You should definitely not access it in the viewmodel repoimpl: The whole idea of ​​splitting the repository into interfaces and implementations is to hide the implementation

  7. It is a good idea to define a screen-composable, it does not require a viewmodel, only data, similar to https://developer.android.com/jetpack/compose/state#state-hoisting

.

@Composable
private fun FavScreen(
  val viewModel: MovieViewModel,
) {
  FawScreen(
    uiState = viewModel.fav.collectAsStateWithLifecycle().value,
    shouldShowDialog = viewModel.shouldShowDialog.collectAsStateWithLifecycle().value
  )
}

@Composable
private fun FavScreen(
  val uiState: UiStates,
  val shouldShowDialog: Boolean
) {
  // your current ui
}

The above is the detailed content of UI not reassembled after deleting all items in jetpack using room database. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete