i have a issue with a lazy column and a database. When i load my activity with a lazy column, the database is call to get list of tournaments. But the list is not display in the lazy column. The list is display the moment after i click to change of page/activity although it makes sense.
DAO:
@Dao
interface TournamentDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(tournament: TournamentEntity)
@Update
suspend fun update(tournament: TournamentEntity)
@Delete
suspend fun delete(tournament: TournamentEntity)
@Query("SELECT * from tournaments WHERE id = :id")
fun getItem(id: Int): Flow<TournamentEntity>
@Query("SELECT * from tournaments ORDER BY id ASC")
fun getAllItems(): Flow<List<TournamentEntity>>
}
REPOSITORY:
class OfflineTournamentRepository(private val tournamentDao: TournamentDao):TournamentRepository {
override fun getAllStream(): Flow<List<TournamentEntity>> = tournamentDao.getAllItems()
override fun getStream(id: Int): Flow<TournamentEntity?> = tournamentDao.getItem(id)
override suspend fun insert(tournament: TournamentEntity) = tournamentDao.insert(tournament)
override suspend fun delete(tournament: TournamentEntity) = tournamentDao.delete(tournament)
override suspend fun update(tournament: TournamentEntity) = tournamentDao.update(tournament)
}
VIEW MODEL:
class TournamentListViewModel(private val tournamentRepository: TournamentRepository):ViewModel() {
private val _uiState = MutableStateFlow(TournamentListState())
var uiState: StateFlow<TournamentListState> = _uiState.asStateFlow()
private set
fun updateUiState(tournaments: List<Tournament>) {
_uiState.value.tournaments=tournaments
}
private suspend fun getAll(){
_uiState.value.tournaments=tournamentRepository.getAllStream().first().map { it.toModel() }
}
init{
viewModelScope.launch {
getAll()
}
}
}
STATE:
class TournamentListState(
var tournaments: List<Tournament> = listOf(),
) {
}
LAZY COLUMN OF ACTIVITY:
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.91f)
.padding(0.dp, 10.dp, 0.dp, 0.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(tournamentListViewModel.uiState.value.tournaments){ tournament: Tournament ->
TournamentItemCard(
tournament = tournament,
modifier = Modifier
.fillParentMaxWidth()
.fillParentMaxHeight(0.1f)
)
}
}
Please what i have to change for the list display when data arrives?
I think the reason might be you are not observing state. Which in turn doesn't know the inner property (tournaments) changes of your uiState variable. Compose needs state to recompose. So, as it doesn't know your state change and it wasn't recomposing to show the data. When the state changes (e.g. after click) the it recomposes with the already loaded data thus showing the list.
The resolution is that, you observe your state change in your compose side and update the state in your StateHolder (ViewModel in your case) whenever you get new data.
The below way will show your list in UI whenever it's available in ViewModel side with some minor optimization.
Change your TournamentListViewModel
to:
class TournamentListViewModel(private val tournamentRepository: TournamentRepository):ViewModel() {
private val _uiState = MutableStateFlow(TournamentListState())
val uiState: StateFlow<TournamentListState> = _uiState.asStateFlow() // <- Changed to val as it doesn't need to be Mutable and being accessed outside StateHolder
fun updateUiState(tournaments: List<Tournament>) {
_uiState.value.tournaments = tournaments
}
private suspend fun getAll() {
_uiState.value = _uiState.value.copy(tournaments = tournamentRepository.getAllStream().first().map { it.toModel() } // <- Changed to updating state with new value instead of inner property so that your UI is notified of data changes
}
init {
viewModelScope.launch {
getAll()
}
}
}
Changing your TournamentListState
class into data class
. Also add state observing property in your UI side (LazyColumn's place in your case) like below.
val uiState = tournamentListViewModel.uiState.collectAsState()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.91f)
.padding(0.dp, 10.dp, 0.dp, 0.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(uiState.value.tournaments){ tournament: Tournament ->
TournamentItemCard(
tournament = tournament,
modifier = Modifier
.fillParentMaxWidth()
.fillParentMaxHeight(0.1f)
)
}
}