I did a `viewmodel` with `Flow` and `Room` that co...
# compose
p
I did a
viewmodel
with
Flow
and
Room
that connected my variables on the
UiState
with
Room
using
.first()
as the codelabs showed me. I noticed that using
.first()
is not a good idea because when I inserted something in the database, the screen didn't get refreshed automatically showing the new value. So I learned that using
collect{}
is the correct approach to continuously update the uistate variables automatically. After changing it now my screen is not showing any data and I can't disscover why. Please, can you help me trying to find where is the issue? I'm adding the viewmodel here.
You can see the old code commented. I mean the code with .first() that worked, but without updating automatically. The commented code is followed by the new code with collect{} that didn't work at all.
Copy code
data class UiState(
    val searchResult: List<Airport> = listOf(),
    val favorites: List<Favorite> = listOf(),
    val selectedAirport: Airport? = null,
    val flightsForSelectedAirport: List<Airport> = listOf()
)

class FlightsScreenViewModel(
    private val flightRepository: FlightsRepository,
    private val userPreferencesRepository: UserPreferencesRepository
) : ViewModel() {
    var uiState by mutableStateOf(UiState())
        private set

    var searchText by mutableStateOf("")
        private set

    init {
        viewModelScope.launch {
//          searchText = userPreferencesRepository.searchText.first()
            userPreferencesRepository.searchText.collect{ searchText = it }
            updateSearchResults()
        }
    }

    private suspend fun updateSearchResults() {
//        val searchResult = if (searchText != "")
//            flightRepository.getAirportsByIatOrName(searchText).filterNotNull().first()
//        else
//            emptyList()
//
//        val favorites = flightRepository.getFavorites().filterNotNull().first()
//
//        uiState = uiState.copy(
//            searchResult = searchResult,
//            favorites = favorites
//        )

        if (searchText != "") {
            flightRepository.getAirportsByIatOrName(searchText).filterNotNull().collect{ uiState = uiState.copy(searchResult = it) }
        } else {
            uiState = uiState.copy(favorites = emptyList())
        }

        flightRepository.getFavorites().filterNotNull().collect{ uiState = uiState.copy(favorites = it) }
    }

    fun updateSearchText(searchText: String) {
        this.searchText = searchText
        viewModelScope.launch {
            userPreferencesRepository.saveSearchTextPreference(searchText)
            updateSearchResults()
        }
    }

    fun selectAirport(airport: Airport?) {
        viewModelScope.launch {
//            val flightsForSelectedAirport = if (airport == null) {
//                emptyList()
//            } else {
//                flightRepository.getAllDifferentAirports(airport.id).first()
//            }
//
//            uiState = uiState.copy(
//                selectedAirport = airport,
//                flightsForSelectedAirport = flightsForSelectedAirport
//            )

            if (airport != null) {
                flightRepository.getAllDifferentAirports(airport.id).collect{ uiState = uiState.copy(flightsForSelectedAirport = it) }
            } else {
                uiState = uiState.copy(flightsForSelectedAirport = emptyList())
            }

            uiState = uiState.copy(selectedAirport = airport)
        }
    }

    fun insertFavorite(depart: String, arrive: String) {
        if (!uiState.favorites.checkIfFavoriteExists(depart, arrive)) {
            val favorite = Favorite(departureCode = depart, destinationCode = arrive)
            viewModelScope.launch {
                flightRepository.insertFavorite(favorite)
            }
        }
    }

    companion object {
        val factory : ViewModelProvider.Factory = viewModelFactory {
            initializer {
                FlightsScreenViewModel(
                    FlightSearchApplication().container.flightRepository,
                    FlightSearchApplication().container.userPreferencesRepository
                )
            }
        }
    }
}

fun List<Favorite>.checkIfFavoriteExists(depart: String, arrive: String): Boolean{
    for (favorite in this){
        if (favorite.departureCode == depart && favorite.destinationCode == arrive)
            return true
    }
    return false
}
This is the code that should show the content in the screen, but is not showing nothing after updating from .first() to .collect{}
Copy code
LazyColumn(
    modifier = Modifier.padding(8.dp)
) {
    items(uiState.searchResult) { airport ->
        AirportDetail(airport, onAirportSelected)
    }
}
Copy code
uiState.selectedAirport?.let {
    FlightsForAirport(
        airport = uiState.selectedAirport,
        arrivals = uiState.flightsForSelectedAirport,
        favorites = uiState.favorites,
        onFavoriteSelected = onFavoriteSelected
    )
}
l
In your
updateSearchResults
function, you have two
flow.collect()
in same suspend function which would be dangerous as for some types of flow mostly hot flow like
StateFlow
or
SharedFlow
, the
collect()
suspends there forever until got canceled. So that might be the reason.