android-jetpack-composecompose-desktop

Issue with Canvas in Jetpack Compose not clearing drawn content when setting StateFlow to null


I'm currently working on a Jetpack Compose project where I've implemented a custom chart using the Canvas API. I'm utilizing a MutableStateFlow<List<Offset>?> to represent a list of points that I draw on the Canvas. The drawing logic is handled within the Canvas Composable.

I've encountered an issue when I set the list of points to null in my ViewModel using _points.emit(null). Despite setting the points to null, the Canvas does not seem to understand that it should clear the previously drawn content.

Here's a simplified version of the relevant code:

private val _points = MutableStateFlow<List<Offset>?>(null)
val points: StateFlow<List<Offset>?>
    get() = _points

private fun updatePoints() = uiEventScope.launch {
    val pointList = ArrayList<Offset>()
    // Some logic to populate the list...
    _points.emit(pointList)
}

private fun removePoints() = uiEventScope.launch {
    // Some logic to clear the list or set it to null...
    _points.emit(null)
}

val points by viewModel.points.colectAsState()
Canvas(
    modifier = Modifier.fillMaxSize()
) {
    points?.apply {
        for (i in 0 until points.size - 1) {
            // Drawing logic...
        }
    }
}

I expected that setting the points to null would clear the Canvas, but it seems like the previous content persists. I would appreciate any insights into why this might be happening and how I can ensure that the Canvas clears its content when the StateFlow is set to null.

Thank you for your help!


Solution

  • We need to handle the case where the list is null. I added an empty rect with a background color when it is null. You can change this however you want. But you need to handle the situation where null is returned with a logic similar to this. You didn't add where you collected the flow. If you are not using collectAsState you may need to adapt this logic where you collect.

    val backgroundColor = MaterialTheme.colorScheme.background 
    Canvas(
        modifier = Modifier
            .fillMaxSize()
            .drawBehind {
                if (points.value == null) {
                    drawRect(backgroundColor, size = size)
                    return@drawBehind
                }
                points.value?.let { pointList ->
                    for (i in 0 until pointList.size - 1) {
                        // Drawing logic...  
                    } 
                }
            }
    ) {
     
    }