I'm trying to draw a backgroud (5000 dots using drawPoints
) that shouldn't change once it was drawn. Every time I modify the count
variable (which is not read by Box1
), Box1
still redraws, though only Box2
reads count
variable.
Related code:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestProjectTheme {
Scaffold(
containerColor = Color(0xFF5E5E5E),
modifier = Modifier.fillMaxSize()
) { innerPadding ->
val count = remember { mutableIntStateOf(0) }
val incrementCounter = { count.intValue++ }
Box(
Modifier
.size(300.dp)
.background(Color.Red)
.padding(innerPadding)
) {
// Box1
Box(Modifier
.size(300.dp)
.drawWithCache {
onDrawBehind {
Log.e("-->", "DrawBehind") // <-- Why is this called even if only Box2 content changed?
}
}
.pointerInput(1, 2) {
detectTapGestures { offset ->
incrementCounter()
}
}) {}
// Box2
Box(
Modifier
.size(300.dp)
) {
repeat(count.intValue) {
Text("$it")
}
}
}
}
}
}
}
}
How to prevent recomposition of Box1
after modifing counter?
Is there a better way of drawing a static background consisting of 5000 dots?
It happens because of 2 reasons. First one is scopping. Any non-inline @Composable function that returns Unit is a scope in Jetpack Compose. You can refer this answer for more details.
In you case closest scope is Scaffold, By adding a Composable with scope you can limit recomposition for a smaller scope
@Composable
fun MyBox(
content: @Composable () -> Unit,
) {
Box {
content()
}
}
@Preview
@Composable
fun Test() {
val count = remember { mutableIntStateOf(0) }
val incrementCounter = { count.intValue++ }
SideEffect {
println("Recomposed with ${count.value}")
}
Box(
Modifier
.size(300.dp)
.background(Color.Red)
) {
// Box1
Box(Modifier
// .graphicsLayer()
.size(300.dp)
.drawWithCache {
onDrawBehind {
println("DrawBehind")
}
}
.pointerInput(1, 2) {
detectTapGestures { offset ->
incrementCounter()
}
}) {}
// MyBox {
// Box2
Box(
Modifier
.size(300.dp)
) {
repeat(count.intValue) {
Text("$it")
}
}
// }
}
}
Uncomment MyBox in the example above and you won't see any logs after recomposition in SideEffect
.
Second issue is or how Jetpack Compose works is everything is drawn to same layer unless you use Modifier.graphicsLayer or any Modifier such as clipToBounds, alpha, rotate, etc that calls Modifier.graphics under the hood.
If you add new layer for, uncomment graphicsLayer(), you will see that that log inside draw Modifier won't be called either. You can refer this link for my question in detail that shows irrelevant composable calling draw based on same issue or how compose works.