google / accompanist

A collection of extension libraries for Jetpack Compose
https://google.github.io/accompanist
Apache License 2.0
7.43k stars 598 forks source link

Modifier.placeholder sometimes prevent Coil from loading image #1587

Closed PhilipDukhov closed 1 year ago

PhilipDukhov commented 1 year ago

Describe the bug

When I use Coil image to load multiple images, with Modifier.placeholder applied to display the loading status, some of them fail to complete the loading.

It is strange that I need so many circumstances to reproduce this: if I use any other view in the if (url == null) block, the error will not happen. A scrollable container and Modifier.alpha(0.9f) are also necessary.

In my example I use the same image, but the error persists when different urls are used.

I've debugged it and it looks like it happens because of this code block - since Coil can't start drawing until Painter.onDraw is called, which won't happen(or may not happen?) if you don't call drawContent. I tried changing condition contentAlpha in 0.01f..0.99f to contentAlpha < 0.99f, and it solves the problem.

To Reproduce

@Composable
fun TestView() {
    var url by remember { mutableStateOf<String?>(null) }
    LaunchedEffect(Unit) {
        delay(1000)
        url = "https://download.samplelib.com/png/sample-boat-400x300.png"
    }

    Row(
        modifier = Modifier
            .horizontalScroll(rememberScrollState())
    ) {
        if (url == null) {
            TestCell(null)
        } else {
            TestCell(url)
        }
    }
}

@Composable
private fun TestCell(url: String?) {
    Row {
        repeat(3) {
            val modifier = Modifier
                .alpha(0.9f)
                .size(100.dp)
                .background(Color.Gray)
            if (url != null) {
                AsyncImage(url, contentDescription = null, modifier = modifier)
            } else {
                Box(modifier)
            }
        }
    }
}

@Composable
private fun AsyncImage(
    data: Any?,
    modifier: Modifier = Modifier,
    contentDescription: String?,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
) {
    val painter = rememberAsyncImagePainter(
        model = ImageRequest.Builder(LocalContext.current)
            .data(data)
            .crossfade(true)
            .build(),
        contentScale = contentScale,
    )
    val shimmer by remember(painter) {
        derivedStateOf {
            painter.state is AsyncImagePainter.State.Loading
        }
    }
    Image(
        painter = painter,
        contentDescription = contentDescription,
        contentScale = contentScale,
        alignment = alignment,
        modifier = modifier
            .placeholder(
                visible = shimmer,
                color = Color.Gray,
            )
    )
}

Expected behavior

I expect all three image views to be displayed when loading finishes. The bug also reproduces when app restarts and coil is loading from disk cache, so it's not network error.

Screenshots?

With my sample code first image is always in loading state:

Environment:

Nek-12 commented 1 year ago

Doesn't coil have its own placeholders?

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.