I have an Android ViewModel that manages a recyclerView with ImageView in each row where I'm setting a userId string in a StateFlow<String?>. I want to use that userId to retrieve the user's description as another StateFlow. I'm doing this:
class MyViewModel : ViewModel {
private val _myItems = MutableStateFlow<MyItem>(emptyList())
val myItems: StateFlow<List<MyItems>
get() = _myItems
private val _userId = MutableStateFlow<String?>(null)
suspend fun getMyItems() {
viewModelScope.launch(Dispatchers.IO) {
getMyItemsUseCase().collect {
_myItemsStateFlow.value = it
}
}
}
fun setUserId(userId: String) {
_userId.value = userId
}
@OptIn(ExperimentalCoroutinesApi::class)
val userDescription: StateFlow<String?> = _userId.flatMapLatest { userId ->
userId?.let { id -> getUserDescriptionUseCase(id)} ?: emptyFlow()
}.stateIn(viewModelScope, SharingStarted.Lazily, null)
}
In my recyclerView adapter, I'm doing this:
viewHolder.descriptionTextView
.findViewTreeLifecycleOwner()?
.lifecycleScope?.launch {
viewModel.userDescription.collect { d ->
viewHolder.descriptionTextView.setText(d)
}
}
}
viewModel.setUserId(userId)
However, the problem I'm encountering is that each row is showing the same description, even though each row is a different user with a different userId.
Obviously I'm doing something wrong with the Flows... but I don't know what. Is it because the Flow is being shared by the different rows in the recyclerView?
*** UPDATE: Adding more code as requested in comments ***
data class MyItem(val userId:String, ... other params not shown)
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewModel.getMyItems().collect { myItems ->
(recyclerView.adapter as MyRecyclerAdapter).setData(myItems)
}
}
}
}
class MyRecyclerAdapter(private val viewModel: MyViewModel) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
internal inner class ViewHolder(val binding: MyLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
val userIdTextView = binding.userIdTextView
val descriptionTextView = binding.descriptionTextView
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
data.get(posiiton).let {
val userId = it.userId
viewModel.setUserId(userId)
viewHolder.userIdTextView.text = userId
viewHolder.descriptionTextView
.findViewTreeLifecycleOwner()?
.lifecycleScope?.launch {
viewModel.userDescription.collect { d ->
viewHolder.descriptionTextView.setText(d)
}
}
viewModel.setUserId(userId)
}
}
fun setData(data: List<MyItem>) {
this.data = data
notifyDataSetChanged()
}
}