I have an app where the users screen is made up of a list of objects from the DB. Those objects are updated in the background business logic code. The composable is filled correctly on first compose but is never updated. Breakpoints in the VMs collect are fired when upon initial composition, but not when the tables are updated as verified with App Inspection.
I've removed the extra fields and code that maps everything to the UI for brevity.
Any help would be appreciated!!!
The Entity
@Entity(primaryKeys = {"participantId", "studyId"})
public class Participant {
//******
//Required parameters
//******
@NonNull @ColumnInfo
public String participantId;
@NonNull @ColumnInfo
public String studyId;
//Other fields go here
public Participant(@NonNull String participantId, @NonNull String studyId) {
this.participantId = participantId;
this.studyId = studyId;
}
}
The Dao
@Dao
interface ParticipantDao {
@Insert
fun insertParticipant(participant: Participant): Long
@Delete
fun deleteParticipant(participant: Participant)
@Query("SELECT * FROM Participant ORDER BY participantId ASC")
fun getAllParticipants(): List<Participant>
}
The Db helper
interface DatabaseHelper {
fun getAllSensors(): Flow<List<Sensor>>
fun getAllParticipants(): Flow<List<Participant>>
}
class AppDatabaseHelper(private val appDatabase: AppDatabase) : DatabaseHelper{
override fun getAllSensors(): Flow<List<Sensor>> = flow {
emit(appDatabase.sensorDao().getAllSensors())
}
override fun getAllParticipants(): Flow<List<Participant>> = flow {
emit(appDatabase.participantDao().getAllParticipants())
}
}
The composable
@Composable
fun Users(navController: NavHostController, usersVM: UsersVM){
val uiState by usersVM.uiState.collectAsState()
usersVM.navController = navController
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
if(uiState.participants.isNotEmpty()) {
for(participant in uiState.participants) {
//Display the users
}
}
}
}
The VM
class UsersVM(private val appDB: AppDatabase): ViewModel() {
private val _uiState = MutableStateFlow(UsersUiState())
val uiState: StateFlow<UsersUiState> = _uiState.asStateFlow()
var participants: List<Participant> = listOf()
private var appDBHelper: AppDatabaseHelper = AppDatabaseHelper(appDB)
init {
viewModelScope.launch {
appDBHelper.getAllParticipants()
.flowOn(Dispatchers.IO)
.catch { e ->
Timber.e("fillUsersList getAllParticipants ex: ${e.message}")
}.collect {
participants = it
_uiState.value = UsersUiState(participants)
}
}
}
}
data class UsersUiState(val participants: List<Participant> = mutableListOf(), val sensors: List<Sensor> = mutableListOf())
The Dao is returning a List
and not a Flow
.
For observable data, you have to return Flow
from Dao.
Dao
@Dao
interface ParticipantDao {
@Query("SELECT * FROM Participant ORDER BY participantId ASC")
fun getAllParticipants(): Flow<List<Participant>>
}
class AppDatabaseHelper(private val appDatabase: AppDatabase) : DatabaseHelper{
override fun getAllParticipants(): Flow<List<Participant>> = appDatabase.participantDao().getAllParticipants()
}
Reference
https://developer.android.com/training/data-storage/room/async-queries#observable