I am completely new to Jetpack Compose AND Kotlin, but not to Android development in Java. Wanting to make first contact with both technologies, I wanted to make a really simple app which populates a LazyColumn with images from Dog API.
All the Retrofit connection part works OK, as I've managed to populate one card with a random puppy, but when the time comes to populate the list, it's just impossible. This is what happens:
dogImages gets updated automatically.Do you have any ideas? I can't find any tutorial on this matter, just vague explanations about state for scroll listening.
Here's my code:
class MainActivity : ComponentActivity() {
    private val dogImages = mutableStateListOf<String>()
    @ExperimentalCoilApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PuppyWallpapersTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    DogList(dogImages)
                    searchByName("poodle")
                }
            }
        }
    }
    private fun getRetrofit():Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://dog.ceo/api/breed/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    private fun searchByName(query: String) {
        CoroutineScope(Dispatchers.IO).launch {
            val call = getRetrofit().create(APIService::class.java).getDogsByBreed("$query/images")
            val puppies = call.body()
            runOnUiThread {
                if (call.isSuccessful) {
                    val images = puppies?.images ?: emptyList()
                    dogImages.clear()
                    dogImages.addAll(images)
                }
            }
        }
    }
    @ExperimentalCoilApi
    @Composable
    fun DogList(dogs: SnapshotStateList<String>) {
        LazyColumn() {
            items(dogs) { dog ->
                DogCard(dog)
            }
        }
    }
    @ExperimentalCoilApi
    @Composable
    fun DogCard(dog: String) {
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .padding(15.dp),
            elevation = 10.dp
        ) {
            Image(
                painter = rememberImagePainter(dog),
                contentDescription = null
            )
        }
    }
}
Thank you in advance! :)
Your view of the image cannot determine the aspect ratio before it loads, and it does not start loading because the calculated height is zero. See this reply for more information.
Also a couple of tips about your code.
MainActivity is bad practice, you can use view models. Inside a view model you can use viewModelScope, which will be bound to your screen: all tasks will be cancelled, and the object will be destroyed when the screen is closed.searchByName. This code can be called many times during recomposition, so your call will be repetitive. You should do this with side effects. In this case you can use LaunchedEffect, but you can also do it in the init view model, because it will be created when your screen appears.MainActivity is not very convenient. A good practice is to store them simply in a file, and separate them logically by files.Your code can be updated to the following:
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PuppyWallpapersTheme {
                DogsListScreen()
            }
        }
    }
}
@Composable
fun DogsListScreen(
    // pass the view model in this form for convenient testing
    viewModel: DogsModel = viewModel()
) {
    // A surface container using the 'background' color from the theme
    Surface(color = MaterialTheme.colors.background) {
        DogList(viewModel.dogImages)
    }
}
@Composable
fun DogList(dogs: SnapshotStateList<String>) {
    LazyColumn {
        items(dogs) { dog ->
            DogCard(dog)
        }
    }
}
@Composable
fun DogCard(dog: String) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(15.dp),
        elevation = 10.dp
    ) {
        Image(
            painter = rememberImagePainter(
                data = dog,
                builder = {
                    // don't use it blindly, it can be tricky.
                    // check out https://stackoverflow.com/a/68908392/3585796
                    size(OriginalSize)
                },
            ),
            contentDescription = null,
        )
    }
}
class DogsModel : ViewModel() {
    val dogImages = mutableStateListOf<String>()
    init {
        searchByName("poodle")
    }
    private fun getRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://dog.ceo/api/breed/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    private fun searchByName(query: String) {
        viewModelScope
            .launch {
                val call = getRetrofit()
                    .create(APIService::class.java)
                    .getDogsByBreed("$query/images")
                val puppies = call.body()
                if (call.isSuccessful) {
                    val images = puppies?.images ?: emptyList()
                    dogImages.clear()
                    dogImages.addAll(images)
                }
            }
    }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With