I need to get the Auto generated ID of a record added to a ROOM database table.
Looking to answer my problem I found the following answer: text
But in my case is not a solution because in Compose I can't use the code:
lifecycleScope.launch
//DAO
@Insert
suspend fun addwarrantyget(warrantyEntity: WarrantyEntity): Long
//Repository
suspend fun addwarrantyget(warrantyEntity: WarrantyEntity):Long {
return inventoryDB.warrantyDao().addwarrantyget(warrantyEntity)
}
//viewModel
fun addwarrantyget(warranty: WarrantyEntity) =
viewModelScope.launch {
repository.addwarrantyget(warranty)
}
//Inside Compose Fun
lifecycleScope.launch {
val id = viewModel.addwarrantyget(warranty)
Log.i("INSERT_ID", "Inserted ID is: $id")
}
As per suggestion and my understanding:
I added:
//viewModel
private val _newItemId = MutableStateFlow(-1L)
val newItemId: StateFlow<Long> = _newItemId
fun addwarrantyget(warranty: WarrantyEntity) = viewModelScope.launch {
val newId = repository.addwarrantyget(warranty)
_newItemId.emit(newId!!)
}
//Compose function extract
Button(
onClick = {
viewModel.addwarrantyget(
WarrantyEntity(
0,
null,
insn,
indescription,
null,
instartdateP.value,
inenddateP.value,
inprovider,
instartdateM.value,
inenddateM.value,
inmanufacturer
)
)
val newItemID by viewModel.newItemId.collectAsStateWithLifecycle()
LaunchedEffect(newItemID) {
if (newItemID == -1L) return@LaunchedEffect
// Do what you need with the new id here
warrantyId.value = viewModel.newItemId
mToast("Warranty Added", mContext)
}
mToast("Warranty Added", mContext)
},
colors = ButtonDefaults.buttonColors(Color.Blue)
) {
Text(text = "Add Warranty")
}
collectAsStateWithLifecycle: gives undefined reference error
warrantyId.value = viewModel.newItemId: gives Require Long Found StateFlow error
LaunchedEffect: gives @Composable invocations can only happen from the context...
In compose you have LaunchedEffect. You won't get the id at the same place you call viewModel.addwarrantyget(warranty)
, but you can consume the new id value from the viewModel
as a state.
viewModel
private val _newItemId = MutableStateFlow(-1)
val newItemId: StateFlow<Int> = _newItemId
fun addwarrantyget(warranty: WarrantyEntity) = viewModelScope.launch {
val newId = repository.addwarrantyget(warranty)
_newItemId.emit(newId)
}
Composable
val newItemID by viewModel.newItemId.collectAsStateWithLifecycle()
LaunchedEffect(newItemID) {
if (newItemID == -1) return@LaunchedEffect
// Do what you need with the new id here
}
Note that i set filtered out the default newItemID
value -1
, there are other ways to do it of course.
Edit:
Regarding Require Long Found StateFlow
error. The newItemID
already contains the value of the new id, you don't need to read it from the viewModel
again.
mToast("New id: $newItemID", mContext)
Please read about States to gain better understanding of what is going on here.
About @Composable invocations can only happen from the context...
error. As the error says, LaunchedEffect
(as a composable function) can not be called from the onClick
lambda, which is not a composable function. It's important to understand that consuming newItemId
state and calling viewModel.addwarrantyget()
are not tied together. You can put the LaunchedEffect
to your composable screen level instead.
@Composable
fun YourScreen(
viewModel: YourViewModel = viewModel()
) {
// other code
val newItemID by viewModel.newItemId.collectAsStateWithLifecycle()
LaunchedEffect(newItemID) {
if (newItemID == -1) return@LaunchedEffect
mToast("New id: $newItemID", mContext)
}
// other composables
}