0

I have a list of users. Each user has a profile picture in storage that has a URL like this:

gs://my-app.appspot.com/users/uid.png

This list is displayed in a LazyRow:

LazyRow(
    modifier = Modifier.fillMaxWidth()
) {
    items(users) { user ->
        LaunchedEffect(user.id) {
            viewModel.getDownloadUrl(user.uid)
        }
        when(val response = viewModel.response) {
            is Result.Loading -> Unit
            is Result.Success -> {
                user.downloadUrl = response.data
                UserCard(
                    user = user
                )
            }
            is Result.Failure -> print(response.e)
        }
    }
}

Since common libraries doesn't know how to read that reference, I need to convert to a real URL. So at each iteration, I create a call to get the download URL. The problem is that I get the same picture for all users and if scroll right, I get other pictures, but doubled, tripled. I don't understand why. How to overcome this situation?

Joan P.
  • 2,368
  • 6
  • 30
  • 63
  • The usage of `LaunchedEffect` is not as it is intended to be used. Fetching download URL is a domain logic and should be completely handled in ViewModel or other layers. The code should not have any interaction with UI layer. (Jetpack compose or View based UI) – Abhimanyu Aug 20 '22 at 12:54
  • Thanks for commenting, but I'm not sure I understand. I am getting the download URL inside the ViewModel class using `viewModel.getDownloadUrl(user.uid)`, which in terms calls another method that exists in a repository class. So how handle this kind of situation? – Joan P. Aug 20 '22 at 13:03
  • I'm using LaunchedEffect, so I can only call the method inside the ViewModel only once. – Joan P. Aug 20 '22 at 13:04
  • But, the problem is the `LaunchedEffect` is used in a loop. `items` are a form of loops. – Abhimanyu Aug 20 '22 at 13:05
  • You have `users` in the composable, I assume you are somehow emitting it from the View Model and collecting it in the Composable? In the same way, you can emit another list with the download URL or modify users to have the download URL. Whichever is apt for your usecase. – Abhimanyu Aug 20 '22 at 13:06
  • Yes, that's exactly what I'm doing. I'm emitting a Result object that can contain the user list or an error. And in the composable , I just set the `downloadUrl` inside the user object. So you basically say that `LaunchedEffect` is causing the problem, right? The user in the database doesn't contain that URL, that's why I need to get it on the client using the UID. Any other solutions? – Joan P. Aug 20 '22 at 13:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/247411/discussion-between-abhimanyu-and-joan-p). – Abhimanyu Aug 20 '22 at 13:21
  • @Abhimanyu Thanks again, Abhimanyu. If you want, you can post the solution in the chat, as an answer. It helped me. – Joan P. Aug 20 '22 at 13:31
  • 1
    Added it as an answer. – Abhimanyu Aug 20 '22 at 13:40

1 Answers1

1

Fetching download URLs is domain logic. So it should be handled in ViewModel, Repository, or Data Source wherever appropriate according to the project.

For example, if we decide to handle it in ViewModel.

We would be fetching the list of Users from the repository in the ViewModel. Once we have the list, we can create a new Model class that has all the data from the User model class required in the UI layer along with the download URL.

Then we can emit that list from ViewModel and collect it in the UI as use as required.

Alternatively, we can also create a new list of download URLs and emit both the list of users fetched from the repository and the list of download URLs created in the ViewModel.

In the ViewModel, we can collect both lists to use as required.

The download URL of the nth user will be the nth item in the list.

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121