I’m building a calculator app in Jetpack Compose and facing an issue with recomposition. My layout has three main components: CalculatorDisplay
, CalculatorAdvanced
, and CalculatorPad
. The layout uses weights for responsive design. However, when I toggle theisExpanded
state using CalculatorExpandButton
, the whole screen recomposes instead of only the relevant components (CalculatorAdvanced and CalculatorPad)
.
My goal:
- Prevent
CalculatorDisplay
from recomposing unnecessarily. - Ensure only
CalculatorAdvanced
andCalculatorPad
recompose whenisExpanded
changes.
What I’ve tried:
- Using
remember
andderivedStateOf
: I applied these toadvancedWeight
andpadWeight
to minimize state updates, but the entire UI still recomposes. (Maybe I did it incorrectly) - Inspecting with Layout Inspector: It confirms that pressing
CalculatorExpandButton
causes the wholePhonePortraitLayout
to recompose.
Here’s the code for the layout:
@Composable
fun CalculatorScreen(viewModel: CalculatorViewModel = viewModel()) {
val context = LocalContext.current
val activity = context as Activity
val window = activity.window
val windowSizeClass = rememberWindowSizeClass()
val color = MaterialTheme.colorScheme.surfaceColorAtElevation(24.dp).toArgb()
SideEffect {
window.statusBarColor = color
}
val expandedStateHandler = remember {
ExpandedStateHandler()
}
Box(modifier = Modifier.fillMaxSize()) {
when {
/** PHONE */
windowSizeClass.isPhonePortrait -> PhonePortraitLayout(
viewModel = viewModel,
windowSizeClass = windowSizeClass,
expandedStateHandler = expandedStateHandler
)
}
}
}
class ExpandedStateHandler {
var isExpanded by mutableStateOf(false)
private set
fun toggle() {
isExpanded = !isExpanded
}
}
@Composable
fun PhonePortraitLayout(
viewModel: CalculatorViewModel,
windowSizeClass: MyWindowSizeClass,
expandedStateHandler: ExpandedStateHandler
) {
val displayWeight = 0.3337f // Fixed weight for CalculatorDisplay
val advancedWeight = if (expandedStateHandler.isExpanded) 0.1451f else 0.045f
val padWeight = if (expandedStateHandler.isExpanded) 0.4f else 0.5f
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.padding(bottom = 8.dp)
) {
// Stabilized CalculatorDisplay
Box(
modifier = Modifier
.fillMaxWidth()
.weight(displayWeight)
) {
CalculatorDisplay(
viewModel = viewModel,
modifier = Modifier.fillMaxSize()
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.weight(advancedWeight)
) {
Box(
modifier = Modifier
.weight(3.1f) // Keeps internal proportions
.padding(start = 16.dp, end = 4.dp)
) {
CalculatorAdvanced(
viewModel = viewModel,
windowSizeClass = windowSizeClass,
isExpanded = expandedStateHandler.isExpanded,
modifier = Modifier.fillMaxSize()
)
}
Box(
contentAlignment = Alignment.TopEnd,
modifier = Modifier
.padding(start = 4.dp, end = 16.dp)
.weight(0.56f)
) {
CalculatorExpandButton(
text = if (expandedStateHandler.isExpanded) "Adv" else "Basic",
onClick = { expandedStateHandler.toggle() }
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.weight(padWeight)
.padding(horizontal = 16.dp)
) {
CalculatorPad(
viewModel = viewModel,
windowSizeClass = windowSizeClass,
modifier = Modifier.fillMaxSize()
)
}
}
}