Home > Software design >  LazyColumn with Image from bitmap source blinking / flashing
LazyColumn with Image from bitmap source blinking / flashing

Time:10-05

I'm new to Jetpack Compose. I am currently developing a chat application. I ask the user to choose an image from the gallery or take a picture from the camera. Then I save the file Uri to the database and then listen to the list of all messages. When this list is updated, this image is recomposing and it flashes.

Messages list in viewmodel:

private var _messages = MutableStateFlow<List<ChatUiMessage>>(mutableListOf())
val messages: StateFlow<List<ChatUiMessage>> = _messages
...
private fun observeMessages() {
    viewModelScope.launch {
        chatManager.observeMessagesFlow()
            .flowOn(dispatcherIO)
            .collect {
                _messages.emit(it)
            }
    }
}

Main chat screen:

...
val messages by viewModel.messages.collectAsState(listOf())
...
val listState = rememberLazyListState()
    LazyColumn(
        modifier = modifier.fillMaxWidth(),
        reverseLayout = true,
        state = listState
    ) {
        itemsIndexed(items = messages) { index, message ->
            when (message) {
                ...
                is ChatUiMessage.Image -> SentImageBlock(
                    message = message
                )
                ...
            }
        }
    }

My SentImageBlock:

@Composable
private fun SentImageBlock(message: ChatUiMessage.Image) {
    val context = LocalContext.current
    val bitmap: MutableState<Bitmap?> = rememberSaveable { mutableStateOf(null) }
    bitmap.value ?: run {
        LaunchedEffect(Unit) {
            launch(Dispatchers.IO) {
                bitmap.value = try {
                    when {
                        Build.VERSION.SDK_INT >= 28 -> {
                            val source = ImageDecoder.createSource(context.contentResolver, message.fileUriPath.toUri())
                            ImageDecoder.decodeBitmap(source)
                        }
                        else -> {
                            MediaStore.Images.Media.getBitmap(context.contentResolver, message.fileUriPath.toUri())
                        }
                    }
                } catch (e: Exception) {
                    null
                }
            }
        }
    }

    Box(
        modifier = Modifier
            .fillMaxWidth()
            .padding(end = 16.dp, top = 16.dp, bottom = 16.dp)
            .heightIn(max = 200.dp, min = 200.dp)
    ) {
        bitmap.value?.let {
            Image(
                bitmap = it.asImageBitmap(),
                contentDescription = null,
                modifier = Modifier
                    .wrapContentSize()
                    .align(Alignment.CenterEnd)
            )
        }
    }

    StandardText(text = message.sendFileStatus.toString())
    StandardText(text = message.fileType.toString())
}

I have tried several things and either is the Image always blinking.

Example:

enter image description here

CodePudding user response:

LazyColumn reuses views for items using key argument, and by default it's equal to item index. You can provide a correct key(something like message id) for views to be reused correctly:

val messages = listOf(1,2,3)
LazyColumn(
    modifier = modifier.fillMaxWidth(),
    reverseLayout = true,
    state = listState
) {
    itemsIndexed(
        items = messages,
        key = { index, message -> message.id }
    ) { index, message ->
        when (message) {
                ...
            is ChatUiMessage.Image -> SentImageBlock(
                message = message
            )
                ...
        }
    }
}
  • Related