Reputation: 1
In my Android app I want to show only items that are within a group that the user touchs on using jetpack compose, room, kotlin coroutines and flow and update screen when add a new item for that group. I can create groups and associate items for each group with one-to-many relationship room database. I can also show all items from all groups. But I don't know how to show only the items in the group that the user touchs on and update the screen by collecting flow.
Group's Screen:
When I touch on group1, I want to show only group1 items and when I add a new item on this group update screen automaticaly:
Some codes...
Groups Entity:
@Entity
data class Groups(
@PrimaryKey(autoGenerate = true)
val groupId: Int,
val groupName: String,
//more code
)
Groups Dao:
@Dao
interface GroupsDao {
@Query("SELECT * FROM Groups ORDER BY groupId ASC")
fun getGroups(): Flow<List<Groups>>
@Transaction
@Query("SELECT * FROM Groups")
fun getGroupWithItens(): Flow<List<GroupWithItens>>
@Query("SELECT * FROM Groups WHERE groupId = :groupId")
suspend fun getGroup(groupId: Int): Groups
//more code
}
Items Entity:
@Entity
data class Itens(
@PrimaryKey(autoGenerate = true)
val itemId: Int,
val groupOwnerId: Int,
val itemName: String,
//more code
)
Items Dao:
@Dao
interface ItensDao {
@Query("SELECT * FROM 'Itens' ORDER BY itemId ASC")
fun getItens(): Flow<List<Itens>>
@Query("SELECT * FROM 'Itens' WHERE itemId = :itemId")
suspend fun getItem(itemId: Int): Itens
//more code
}
Items ViewModel:
@HiltViewModel
class ItensViewModel @Inject constructor(
private val repo: ItensRepository
) : ViewModel() {
var item by mutableStateOf(Itens(0, 0, NO_VALUE, NO_VALUE))
private set
val itens = repo.getItens()
fun getItem(itemId: Int) = viewModelScope.launch {
item = repo.getItem(itemId)
}
//more code
}
Relationship data class GroupWithItens:
data class GroupWithItens(
@Embedded val groups: Groups,
@Relation(
parentColumn = "groupId",
entityColumn = "groupOwnerId"
)
val itens: List<Itens> // THIS LIST I WANT DO COLLECT
)
GroupWithItens Dao:
@Dao
interface GroupWithItensDao {
@Transaction
@Query("SELECT * FROM Groups WHERE groupId = :groupId")
fun getGroupWithItens(groupId: Int): Flow<List<GroupWithItens>>
}
GroupWithItens Repository:
interface GroupWithItensRepository {
fun getGroupWithItens(groupId: Int): Flow<List<GroupWithItens>>
}
GroupWithItens RepositoryImpl:
class GroupWithItensRepositoryImpl(
private val groupWithItensDao: GroupWithItensDao
) : GroupWithItensRepository {
override fun getGroupWithItens(groupId: Int) = groupWithItensDao.getGroupWithItens(groupId)
}
GroupWithItens ViewModel:
@HiltViewModel
class GroupWithItensViewModel @Inject constructor(
private val repo: GroupWithItensRepository
) : ViewModel() {
var groupWithItens: Flow<List<GroupWithItens>> = flow{GroupWithItens(Groups(0, "", "", LocalDateTime.now()), emptyList())}
fun getGroupWithItens(groupId: Int) = viewModelScope.launch {
groupWithItens = repo.getGroupWithItens(groupId)
}
}
}
ItensScreen:
@Composable
fun ItensScreen(
viewModelItens: ItensViewModel = hiltViewModel(),
viewModelGroups: GroupsViewModel = hiltViewModel(),
viewModelGroupWithItens: GroupWithItensViewModel = hiltViewModel(),
itemId: Int,
groupId: Int,
//more code
) {
val itens by viewModelItens.itens.collectAsState( //
initial = emptyList() // THIS WORKS
) //
val groupWithItens by viewModelGroupWithItens.groupWithItens.collectAsState( //
initial = 0 // HERE I DON'T KNOW HOW TO GET JUST LIST<ITENS>
) //
LaunchedEffect(Unit) {
viewModelItens.getItem(itemId)
viewModelGroups.getGroup(groupId)
viewModelGroupWithItens.getGroupWithItens(groupId)
}
Log.d(
"*****", "groupId: " + groupId
+ "\n" + "itens: " + itens
+ "\n" + "groupWithItens: " + groupWithItens
)
Scaffold(
topBar = {
//more code
},
bottomBar = {
//more code
},
content = { padding ->
ItensContent(
padding = padding,
itens = itens, //<- HERE SHOW ALL ITEMS
deleteItem = { item ->
viewModelItens.deleteItem(item)
}
//more code
)
}
Log output:
groupId: 1
itens: [Itens(itemId=1, groupOwnerId=1, itemName=item1_group1, itemDescription=), Itens(itemId=2, groupOwnerId=1, itemName=item2_group1, itemDescription=), Itens(itemId=4, groupOwnerId=2, itemName=item1_group2, itemDescription=)]
groupWithItens: [GroupWithItens(groups=Groups(groupId=1, groupName=group1, groupDescription=, groupDateCreation=2023-09-22T19:15:41.702022), itens=[Itens(itemId=1, groupOwnerId=1, itemName=item1_group1, itemDescription=), Itens(itemId=2, groupOwnerId=1, itemName=item2_group1, itemDescription=)])]
I want to collect just [Itens(itemId=1, groupOwnerId=1, itemName=item1_group1, itemDescription=), Itens(itemId=2, groupOwnerId=1, itemName=item2_group1, itemDescription=)])] part.
And composable cards:
@Composable
fun ItensContent(
padding: PaddingValues,
itens: List<Itens>,
deleteItem: (item: Itens) -> Unit,
) {
LazyColumn(
//more code
) {
items(
items = itens
) { item ->
ItemCard(
item = item,
deleteItem = {
deleteItem(item)
}
)
}
}
}
@Composable
fun ItemCard(
item: Itens,
deleteItem: () -> Unit,
) {
Card(
// more code
) {
//more code
}
}
So, how can I collect List in GroupWithItens data class with Kotlin Flow? I try things like: groupWithItens.itens or val itens by viewModelGroupWithItens.groupWithItens.collect( itens-> itens = itens ) but no success. I appreciate for your help!!
Upvotes: 0
Views: 662
Reputation: 57053
By default, as you have found, @Realation retrieves ALL children. So you have to override this practice.
To override this practice you would need either a query that trims the list of Itens (via an IN clause something like SELECT * FROM itens WHERE itenId IN (csv_of_ids))
OR to trim the list programatically.
In your case it is probably easier to do the latter (rather than build as CSV for the IN clause).
You will therefore need a function that can take the two parameters. The groupId and also the list of iten Ids. You can then first get the full groupWithItens and then create a a list of the wanted itens and finally return a new GroupWithItens build from the group and the trimmed list.
So first a query to suit getting the single Groups:-
@Query("SELECT * FROM Groups WHERE groupId=:groupId")
fun getSingleGroupWithItens(groupId: Int): GroupWithItens
Then a function e.g.
@Transaction
@Query("")
fun getGroupWithItensFromList(groupId: Int,itensIdLIst: List<Int>): GroupWithItens {
val gwi = getSingleGroupWithItens(groupId = groupId)
val ilist = arrayListOf<Itens>()
for (i in gwi.itens) {
for (id in itensIdLIst) {
if (i.itemId == id) {
ilist.add(i)
}
}
}
return GroupWithItens(groups = gwi.groups,ilist)
}
*Demo
Using:-
db = TheDatabase.getInstance(this)
daoGroups = db.getGroupsDao()
daoItens = db.getItensDao()
val g1Id = (daoGroups.insert(Groups(0,"GRP001"))).toInt()
val g2Id = (daoGroups.insert(Groups(0,"GRP002"))).toInt()
val g3Id = (daoGroups.insert(Groups(0,"GRP003"))).toInt()
val i1Id = (daoItens.insert(Itens(0,g1Id,"I001"))).toInt()
val i2Id = (daoItens.insert(Itens(0,g1Id,"I002"))).toInt()
val i3Id = (daoItens.insert(Itens(0,g1Id,"I003"))).toInt()
val i4Id = (daoItens.insert(Itens(0,g1Id,"I004"))).toInt()
val i5Id = (daoItens.insert(Itens(0,g2Id,"I005"))).toInt()
val i6Id = (daoItens.insert(Itens(0,g3Id,"I006"))).toInt()
val i7Id = (daoItens.insert(Itens(0,g3Id,"I007"))).toInt()
val i8Id = (daoItens.insert(Itens(0,g1Id,"I008"))).toInt()
val ilist1 = listOf(i2Id,i8Id)
val ilist2 = listOf(i5Id)
val ilist3 = listOf(i1Id,i3Id,i4Id)
logGroupWithItens(daoGroups.getGroupWithItensFromList(g1Id,ilist1),"TEST01")
logGroupWithItens(daoGroups.getGroupWithItensFromList(g2Id,ilist2),"TEST02")
logGroupWithItens(daoGroups.getGroupWithItensFromList(g1Id,ilist3),"TEST03")
}
fun logGroupWithItens(gwi: GroupWithItens, tagSuffix: String) {
val sb = StringBuilder()
for (i in gwi.itens) {
sb.append("\n\tNAME is ${i.itemName} ID is ${i.itemId} OWNERGROUP ID = ${i.groupOwnerId}")
}
Log.d("DBINFO_${tagSuffix}","GROUP NAME IS ${gwi.groups.groupName} ID is ${gwi.groups.groupId}. " +
"The GROUP has ${gwi.itens.size} SELECTED ITENS. They are:-${sb}")
}
The Result output to the log includes:-
D/DBINFO_TEST01: GROUP NAME IS GRP001 ID is 1. The GROUP has 2 SELECTED ITENS. They are:-
NAME is I002 ID is 2 OWNERGROUP ID = 1
NAME is I008 ID is 8 OWNERGROUP ID = 1
:-
D/DBINFO_TEST02: GROUP NAME IS GRP002 ID is 2. The GROUP has 1 SELECTED ITENS. They are:-
NAME is I005 ID is 5 OWNERGROUP ID = 2
:-
D/DBINFO_TEST03: GROUP NAME IS GRP001 ID is 1. The GROUP has 3 SELECTED ITENS. They are:-
NAME is I001 ID is 1 OWNERGROUP ID = 1
NAME is I003 ID is 3 OWNERGROUP ID = 1
NAME is I004 ID is 4 OWNERGROUP ID = 1
Upvotes: 0