I'm trying to retrieve a single Client item from my Room database. Every client is displayed in a list, and each client has an edit button on them. When the button is pressed, I would like to retrieve that client from the database by their id. Their details will then be displayed on an edit screen.
My problem arises in actually getting the client from the database. So far I have tried 2 approaches:
Coroutines based approach
I have tried to retrieve the item using coroutine based functions with Room. This approach does "work" to an extent, but in its current form the coroutine ends up retrieving the newly searched for client ***after*** the edit screen has been displayed. This makes it so that when you edit a client, you end up editing the one you tried to edit previously.I have tried to counteract this by using .join(), using viewModelScope.async rather than launch and then attempting to use .await, and a few other ideas, but none of them have worked.
ClientDao.kt
@Dao
interface ClientDao {
@Query("SELECT * FROM tblClient WHERE id = :id")
suspend fun getClientToEdit(id: Int): List<Client>
}
ClientRepository.kt
class ClientRepository(private val clientDao: ClientDao) {
val clientSearchResults = MutableLiveData<List<Client>>()
suspend fun getClientToEdit(id: Int) {
clientSearchResults.value = clientDao.getClientToEdit(id)
}
}
ClientViewModel.kt
class ClientViewModel(application: Application): ViewModel() {
private val repository: ClientRepository
val clientSearchResults: MutableLiveData<List<Client>>
init {
val clientDB = ManagementDatabase.getDatabase(application)
val clientDao = clientDB.clientDao()
repository = ClientRepository(clientDao)
clientSearchResults = repository.clientSearchResults
}
fun getClientToEdit(clientId: Int) = viewModelScope.launch {
repository.getClientToEdit(clientId)
}
}
ManagementApp.kt
ClientScreen(
onEditClient = { id ->
clientViewModel.getClientToEdit(id)
val editClientList: List<Client>? = clientViewModel.clientSearchResults.value
//This looks awful but it works
// It just gets the client details of the selected client
if (editClientList != null) {
if (editClientList.firstOrNull() != null) {
selectedClient = editClientList[0]
If I could just find a way to make it so that clientViewModel.getClientToEdit(id) fully executed before running the rest of the code in ManagementApp.kt, it would work. The problem is I'm not sure how.
Flow based approach:
I didn't really think this approach would work, but it was worth a shot. I have tried to retrieve the item using a flow list, in the same way I have been retrieving the whole list.ClientDao.kt
@Dao
interface ClientDao {
@Query("SELECT * FROM tblClient WHERE id = :id")
fun getClientToEdit(id: Int): Flow<List<Client>>
}
ClientRepository.kt
class ClientRepository(private val clientDao: ClientDao) {
fun getClientSearchResults(id: Int): Flow<List<Client>> =
clientDao.getClientToEdit(id)
}
ClientViewModel.kt
class ClientViewModel(application: Application): ViewModel() {
private val repository: ClientRepository
init {
val clientDB = ManagementDatabase.getDatabase(application)
val clientDao = clientDB.clientDao()
repository = ClientRepository(clientDao)
}
fun getClientToEdit(clientId: Int): LiveData<List<Client>> {
return repository.getClientSearchResults(id = clientId).asLiveData()
}
}
ManagementApp.kt
ClientScreen(
onEditClient = { id ->
val editClientList by clientViewModel.getClientToEdit(id).observeAsState(listOf())
//This looks awful but it works
// It just gets the client details of the selected client
if (editClientList != null) {
if (editClientList.firstOrNull() != null) {
selectedClient = editClientList[0]
The problem with this approach is that .observeAsState gives me the '@Composable invocations can only happen from the context of a @Composable function' error (Although the snippet of code above is actually within a @Composable function).
If anyone could provide some much needed help I would greatly appreciate it. I'm new to Android and have struggled with Room quite a bit, so my apologies if the code isn't really up to scratch. Thank you.