Jetpack compose LazyColumn fails to load Image with fast scrolling


When fast scrolls then lazycolumn fails to load the image and even the placeholder, tried using Coil still the issue is same with Box { Image}, SubComposeAsyncImage, AsyncImage. All three variations image fails to load from server.

@Composable
fun CharactersList(
    modifier: Modifier = Modifier,
    viewModel: MainActivityViewModel = hiltViewModel()
) {
    val lazyPagingItems = viewModel.pagingDataFlow.collectAsLazyPagingItems()

    LazyColumn(
        modifier = modifier
            .fillMaxSize()
            .background(Color.White),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it.id ?: 0 }) {
            val item = lazyPagingItems[it]
            if (item != null) {
                CharacterListItem(item)
            }
        }

        val loadState = lazyPagingItems.loadState
        if (loadState.append is LoadState.Loading) {
            item {
                CircularProgressIndicator(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                )
            }
        }
    }
}


@Composable
fun CharacterListItem(item: Result) {
    Column(modifier = Modifier.background(Color.Black)) {
        AsyncImage(
            model = ImageRequest.Builder(
                LocalContext.current)
                .data(item.image)
                .crossfade(true).crossfade(true)
                .diskCachePolicy(CachePolicy.ENABLED)
                .memoryCachePolicy(CachePolicy.ENABLED)
                .build(),
            contentDescription = null,
            modifier = Modifier
                .align(alignment = Alignment.CenterHorizontally)
                .padding(top = 10.dp, bottom = 10.dp)
                .size(80.dp)
                .clip(CircleShape)
                .aspectRatio(1f),
            placeholder = painterResource(R.drawable.ic_launcher_background)
        )

        ManualImageLoader(item.image)

        SubcomposeAsyncImage(
            model = item.image,
            contentDescription = null,
            modifier = Modifier
                .padding(vertical = 10.dp)
                .size(80.dp)
                .clip(CircleShape),
            loading = {
                // This is ALREADY centered inside the image area by Coil
                Cir

        Text(
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 10.dp, bottom = 10.dp),
            textAlign = TextAlign.Center,
            text = item.name.toString(),
            fontSize = TextUnit(12f, TextUnitType.Sp),
            color = Color.White
        )
    }
}

@Composable
fun ManualImageLoader(imageUrl: String?) {
    val painter = rememberAsyncImagePainter(imageUrl)
    val state = painter.state

    Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
        Image(
            painter = painter,
            contentDescription = null,
            modifier = Modifier
            .padding(top = 10.dp, bottom = 10.dp)
            .size(80.dp)
            .clip(CircleShape)
            .aspectRatio(1f)
        )

        if (state is AsyncImagePainter.State.Loading) {
            CircularProgressIndicator()
        }
    }
}

ViewModel Code:

 val pagingDataFlow: Flow<PagingData<Result>> = Pager(
        config = PagingConfig(pageSize = 20),
        pagingSourceFactory = {
            CharactersPagingSource(apiInterface)
        }
    ).flow.cachedIn(viewModelScope)

PagingDataSource class:

class CharactersPagingSource(
    private val apiInterface: ApiInterface
) : PagingSource<Int, Result>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Result> {
        return try {
            val nextPage = params.key ?: 1
            val response = apiInterface.getPagedCharacters(page = nextPage)

            LoadResult.Page(
                data = response.results,
                prevKey = if (nextPage == 1) null else nextPage - 1,
                nextKey = if (response.results.isEmpty()) null else nextPage + 1
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, Result>): Int? {
        return state.anchorPosition?.let { anchorPos ->
            state.closestPageToPosition(anchorPos)?.prevKey?.plus(1) ?: state.closestPageToPosition(
                anchorPos
            )?.nextKey?.minus(1)
        }
    }
}

As explained above I have tried all the 3 possibilities that I am aware of to make it working by AyncImage, Creating Custom ManualImageLoader, SubcomposeasyncImage. I saw some other SO threads as well where this issue persists with fast scroll where some data gets missed. Is it a common known issue with Compose or something wrong with the implementation?

0
Feb 27 at 7:37 PM
User AvatarCosmic Dev
#android#image#android-jetpack-compose#android-jetpack#coil

No answer found for this question yet.