From 04a7749a07829ddd23d5fa139f8aeffcf0a72dd5 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 3 Jun 2026 20:14:40 -0400 Subject: [PATCH] fix(compose): defer animation reads to draw/layout phase Replace Modifier.scale() with graphicsLayer lambda to defer scale reads to the draw phase instead of triggering recomposition on every frame. Replace .background() with drawBehind for per-frame alpha changes in TypingIndicator dots. Use derivedStateOf to coalesce animated float reads in FlippableCard front/back switching. --- .../bill/customization/components/HueScroller.kt | 7 +++++-- .../components/SaturationBrightnessGrid.kt | 7 +++++-- .../kotlin/com/getcode/ui/components/Flippable.kt | 7 ++++++- .../getcode/ui/components/chat/TypingIndicator.kt | 14 ++++++++------ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/HueScroller.kt b/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/HueScroller.kt index 4f5cbc7fa..3b432fd97 100644 --- a/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/HueScroller.kt +++ b/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/HueScroller.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size @@ -108,7 +108,10 @@ internal fun HueScroller( modifier = Modifier .align(Alignment.Center) .width(thumbWidth) - .scale(scale), + .graphicsLayer { + scaleX = scale + scaleY = scale + }, hsv = currentHsv, ) } diff --git a/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/SaturationBrightnessGrid.kt b/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/SaturationBrightnessGrid.kt index 0d37be794..b9d0985c6 100644 --- a/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/SaturationBrightnessGrid.kt +++ b/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/components/SaturationBrightnessGrid.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.BlendMode @@ -173,7 +173,10 @@ private fun BoxScope.Thumb( ) } .size(thumbSize) - .scale(scale) + .graphicsLayer { + scaleX = scale + scaleY = scale + } .align(Alignment.TopStart) ) { Box( diff --git a/ui/components/src/main/kotlin/com/getcode/ui/components/Flippable.kt b/ui/components/src/main/kotlin/com/getcode/ui/components/Flippable.kt index 8e948375b..1d226a360 100644 --- a/ui/components/src/main/kotlin/com/getcode/ui/components/Flippable.kt +++ b/ui/components/src/main/kotlin/com/getcode/ui/components/Flippable.kt @@ -8,6 +8,9 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer @@ -59,6 +62,8 @@ fun FlippableCard( } } + val showFront by remember { derivedStateOf { rotation.value <= 90f } } + Box( modifier = modifier .graphicsLayer { @@ -67,7 +72,7 @@ fun FlippableCard( } ) { if (cardFace != CardFace.Neither) { - if (rotation.value <= 90f) { + if (showFront) { Box( modifier = Modifier .fillMaxSize() diff --git a/ui/components/src/main/kotlin/com/getcode/ui/components/chat/TypingIndicator.kt b/ui/components/src/main/kotlin/com/getcode/ui/components/chat/TypingIndicator.kt index 94437ede4..d877e6737 100644 --- a/ui/components/src/main/kotlin/com/getcode/ui/components/chat/TypingIndicator.kt +++ b/ui/components/src/main/kotlin/com/getcode/ui/components/chat/TypingIndicator.kt @@ -41,7 +41,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.scale +import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.CompositingStrategy @@ -191,11 +191,13 @@ private fun TypingDots( Box( modifier = Modifier .size(CodeTheme.dimens.grid.x2) - .scale(scale.value) - .background( - color = baseColor.copy(alpha = alpha.value), - shape = CircleShape - ) + .graphicsLayer { + scaleX = scale.value + scaleY = scale.value + } + .drawBehind { + drawCircle(color = baseColor.copy(alpha = alpha.value)) + } ) } }