'How to use `ImageRequest.Builder.target` in the new coil version in jetpack compose?

My Gradle

// Coil
implementation "io.coil-kt:coil-compose:1.4.0"

Problem Description

Previously I used the coil together with Google's accompanist, but when I migrate to the new version of the coil as the documentation suggests I'm having problems with the target method:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.pokedex, PID: 13502
    java.lang.IllegalArgumentException: request.target must be null.
        at coil.compose.ImagePainterKt.rememberImagePainter(ImagePainter.kt:94)
...

Coil Implementation

When browsing the internal code of ImagePainter (coil class) you can see that the target method really needs to be null for some reason:

@Composable
fun rememberImagePainter(
     request: ImageRequest,
     imageLoader: ImageLoader,
     onExecute: ExecuteCallback = ExecuteCallback.Default,
): ImagePainter {
     requireSupportedData(request.data)
     require(request.target == null) { "request.target must be null." }
...

My Code

Here's my component in jetpack compose (the image component is inside a column):

Image(
    modifier = Modifier
        .size(120.dp)
        .align(Alignment.CenterHorizontally),
    painter = rememberImagePainter(
        data = entry.imageUrl,
        builder = {
            crossfade(true)
            target {
                viewModel.calcDominantColor(it) { color ->
                    dominantColor = color
                }
            }
            transformations(CircleCropTransformation())
        },
    ),
    contentDescription = entry.pokemonName
)

I need the target method to do internal operations on my viewModel based on the drawable it passes as a parameter. Can someone help me?



Solution 1:[1]

In Coil 2.0.0 both AsyncImage and rememberAsyncImagePainter have onSuccess callback parameter, using which you can get the drawable as follows:

AsyncImage(
    model = imageURL,
    contentDescription = null,
    onSuccess = { success ->
        val drawable = success.result.drawable
    }
)

Coil 1.4.0 version:

This is intended behaviour since rememberImagePainter sets the target internally.

You can track the painter state, wait for the Success and get the drawable from it. Also use it with LaunchedEffect to prevent re-calculations:

val painter = rememberImagePainter(
    data = imageUrl,
    builder = {
        ...
    },
)
(painter.state as? ImagePainter.State.Success)
    ?.let { successState ->
        LaunchedEffect(Unit) {
            val drawable = successState.result.drawable
            viewModel.calcDominantColor(drawable) { color ->
                dominantColor = color
            }
        }
    }
Image(
    painter = painter,
    contentDescription = "...",
    modifier = Modifier
        ...
)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1