fuad
03/06/2023, 6:21 PMval messages: LiveData<List<Message>> by lazy {
repository.getAllMessages().asLiveData()
}
val users: LiveData<List<User>> by lazy {
repository.getAllUsers().asLiveData()
}
I want to show in my UI a list of messages, and I want to render the usernames for each message. The Message
data class has a property userId
which should match a userId
in User
. I created another data class
/**
* a [Message] with additional user information
*/
data class MessageUiModel (
val message: Message,
val user: User,
val id: String = message._id
) {
constructor(message: Message, user: User) : this(
id = message._id,
message = message,
user = user
)
}
How do I iterate or combine the two flows to create this third flow for display in my UI? I’ve been hitting my head on the wall with .map
, .combine
, .merge
etc.
Thanks so much in advance.Giorgi
03/06/2023, 6:46 PM.combine
? For example, I cam up with this code
val twoFlowsInOne by lazy {
repository.getAllMessages().combine(repository.getAllUsers(), ::Pair).asLiveData()
}
will it work?::Pair
Kevin Worth
03/06/2023, 8:09 PMflowA.flatMapLatest(flowB)
which will cancel/restart when either of the 2 changes (and if just one of them changes - say B - then it will use the latest value of A, and vice versa - as far as I understand)fuad
03/07/2023, 4:40 AMUser
that has a matching `userID`` for each Message
, and create a new stream of the combined objects. I think what's happening is I'm getting back a single object instead of a List.
This is my attempt to combine the two flows in the ViewModel:
val messagesWithUsersFlow: Flow<List<MessageUiModel>> = combine(
repository.getAllUsers(),
repository.getAllMessages()
) { users: List<User>, messages:List<Message> ->
messages.map {
return@combine MessageUiModel.invoke(
message = it,
users = users
)
}
} as Flow<List<MessageUiModel>>
where, I have now updated the data class with an .invoke
to try to handle the parsing:
data class MessageUiModel (
val message: Message,
val user: User,
val id: String = message._id
) {
companion object {
operator fun invoke(message: Message, users: List<User>) : MessageUiModel {
var messageSender: User? = null
for (user in users) {
if (user.id == message.userId) {
messageSender = user
}
}
messageSender?.let {
return MessageUiModel(user = messageSender, message = message)
}
val noUserFound = User()
return MessageUiModel(user = noUserFound, message = message)
}
}
constructor(message: Message, user: User) : this(
id = message._id,
message = message,
user = user
)
}
And then in my Composable UI I try to consume it like this:
val messagesWithUsers : List<MessageUiModel> by viewModel
.messagesWithUsersFlow
.collectAsState(initial = emptyList())
And when I try to run the app, it crashes with this exception:
java.lang.ClassCastException: c..ast to java.util.List
Kevin Worth
03/07/2023, 2:42 PMfuad
03/07/2023, 4:29 PMreturn@combine
and everything works great after that. The return shouldn’t have been in the mapping function