Good afternoon.
I’m trying to solve the exercise proposed by google to learn how to code in kotlin.
The problem statement is the following :
you have a db with “airport” with some information.
You should display them on a screen.
The displayed “airport” depends on what you are currently typing in the research bar.
When you click on a airport, the screen should be updated with the available “flight”.
My code seems to work when I’m not using “Flow”, I’m then using viewModelScope.launch and updating the state. However, from my understanding, it’s possible to get the same results using “Flow” and it seems to be easier and more readable.
But I’m having some issues with “Flow”,
When I type the airport, everything works fine, or it seems so, when I click on a airport, nothing changes on the screen but it seems the values in the viewModel are updated (Log.d). When I remove or I type one more letter in the research bar, my screen is updated and shows the expected results.
There is something that I don’t get with Flow, I expected that the recomposition works when a new value was received from the flow.
Here are the main parts related to this question .
class HomeScreenViewModel (private val airportDao: AirportDao): ViewModel() {
// Game UI state
private val _uiState = MutableStateFlow(HomeScreenUiState())
val uiState: StateFlow<HomeScreenUiState> = _uiState.asStateFlow()
private var searchAirport by mutableStateOf("")
fun updateSearchAirport(searchedAirport : String){
searchAirport = searchedAirport
}
fun updateDisplayedAirports(): Flow<List<Airport>> {
val updatedDisplayedAirports = airportDao.getAirportsByIataCode(searchAirport).combine(airportDao.getAirportsByName(searchAirport)) { list1, list2 ->
// Combine or concatenate the lists as needed
(list1 + list2).distinct()
}
Log.d("updateDisplayedAirpots", updatedDisplayedAirports.toString())
return updatedDisplayedAirports
}
fun updateSelectedAirport(airport : Airport){
_uiState.update {
it.copy(
selectedAirport = airport
)
}
}
fun updatePotentialFlights(): Flow<List<Flight>>{
var flightId = 1
val selectedAirport = _uiState.value.selectedAirport
if (selectedAirport != null) {
val updatedPotentialFlights = airportDao.getAllAirportExcept(selectedAirport.iata_code)
.map { originalList ->
originalList.map { Flight(flightId++, selectedAirport, it) }
}
return updatedPotentialFlights
} else {
return flow {
emit(emptyList<Flight>())
}
}
}
fun isFlightInFavorite(flight: Flight) : Boolean{
return flight in uiState.value.favorite
}
fun getSearchedAirport() : String {
return searchAirport
}
fun updateFavorite(flight: Flight) {
_uiState.update { currentState ->
val updatedFavorites = currentState.favorite.toMutableList()
if (flight in updatedFavorites) {
updatedFavorites.remove(flight)
} else {
updatedFavorites.add(flight)
}
currentState.copy(favorite = updatedFavorites)
}
}
companion object {
val factory : ViewModelProvider.Factory = viewModelFactory {
initializer {
val application = (this[APPLICATION_KEY] as FlightResearchApplication)
HomeScreenViewModel(application.database.AirportDao())
}
}
}
}
data class HomeScreenUiState(
var favorite: MutableList<Flight> = mutableListOf(),
var selectedAirport : Airport ?= null
)
and where I collect the states in my screen
@Composable
fun HomeScreen(
homeScreenViewModel: HomeScreenViewModel = viewModel(factory = HomeScreenViewModel.factory),
onFavoriteClicked: (Flight) -> Unit,
isFlightInFavorite: (Flight) -> Boolean,
onAirportClicked : (Airport) ->Unit,
modifier: Modifier = Modifier) {
val potentialAirports by homeScreenViewModel.updateDisplayedAirports().collectAsState(emptyList())
val potentialFlights by homeScreenViewModel.updatePotentialFlights().collectAsState(emptyList())
// val homeScreenUiState by homeScreenViewModel.uiState.collectAsState()
Column(){
ResearchTopBar(homeScreenViewModel.getSearchedAirport(),
onSearchedAirportChanged = { homeScreenViewModel.updateSearchAirport(it) }
)
LazyColumn(){
if (potentialFlights.isEmpty()){
itemsIndexed(potentialAirports) { _, airport ->
AirportCard(airport,
onAirportClicked = onAirportClicked)}
} else {
itemsIndexed(potentialFlights) { _, flight ->
FlightCard(homeScreenViewModel,
flight,
isFlightInFavorite ,
onFavoriteClicked)
}
}
}
}
}
If it’s easier for you here is the github repo, the branch is “with_flow”
Could you explain me why i got this issue ? I tried to put the output of "updatePotentialFlights() in the HomeScreenState But i got a similar issue.
Thanks in advance for your help.