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!!
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:-
Then a function e.g.
*Demo
Using:-
The Result output to the log includes:-
:-
:-