Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp

// [START android_compose_performance_backwards_write_read_comp_write_layout]
// [START android_compose_performance_backwards_write_layout_bad]
// [START android_compose_performance_backwards_write_layout]
// ❌ BAD: Read in Composition, Written in Layout (onSizeChanged)
@Composable
fun AspectRatioImage(painter: Painter) {
fun BadAspectRatioImage(painter: Painter) {
var calculatedHeight by remember { mutableStateOf(0.dp) }
val density = LocalDensity.current

// State read during COMPOSITION:
Image(
painter = painter,
Expand All @@ -62,34 +62,39 @@ fun AspectRatioImage(painter: Painter) {
}
)
}
// [END android_compose_performance_backwards_write_layout_bad]

// [START android_compose_performance_backwards_write_layout_good]
// ✅ GOOD: Measure and coordinate heights in Phase 2 (Layout) without recomposition
// ✅ GOOD: Measure and calculate aspect ratio height in Phase 2 (Layout) without recomposition
@Composable
fun EqualHeightRow(
content: @Composable () -> Unit,
fun GoodAspectRatioImage(
painter: Painter,
aspectRatio: Float = 16f / 9f,
modifier: Modifier = Modifier
) {
Layout(
content = content,
content = {
Image(
painter = painter,
contentDescription = "Dynamic Image"
)
},
modifier = modifier
) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
val maxHeight = placeables.maxOfOrNull { it.height } ?: 0
layout(
width = placeables.sumOf { it.width },
height = maxHeight
) {
var xPosition = 0
placeables.forEach { placeable ->
placeable.placeRelative(x = xPosition, y = 0)
xPosition += placeable.width
}
val width = constraints.maxWidth
val height = (width / aspectRatio).toInt() // Illustrative, you can use Modifier.aspectRatio()
val imageConstraints = constraints.copy(
minWidth = width,
maxWidth = width,
minHeight = height,
maxHeight = height
)
val placeable = measurables.first().measure(imageConstraints)
layout(width, height) {
placeable.placeRelative(0, 0)
}
}
}
// [END android_compose_performance_backwards_write_layout_good]
// [END android_compose_performance_backwards_write_layout]


// [START android_compose_performance_backwards_write_layout_subcomposition]
// ✅ Okay option: Subcomposition delays Composition until parent constraints are known
Expand All @@ -101,7 +106,6 @@ fun SizedContent() {
}
}
// [END android_compose_performance_backwards_write_layout_subcomposition]
// [END android_compose_performance_backwards_write_read_comp_write_layout]

// [START android_compose_performance_backwards_write_read_comp_write_draw]
// ❌ BAD: Read in Composition, Written in Draw ()
Expand All @@ -121,28 +125,22 @@ fun BadBackwardsWriteDraw() {
// [END android_compose_performance_backwards_write_read_comp_write_draw]

// [START android_compose_performance_backwards_write_read_comp_write_comp]
// [START android_compose_performance_backwards_write_bad_counter]
// ❌ BAD: Direct write in Composable body after read
@Composable
fun BadCounter() {
var count by remember { mutableIntStateOf(0) }
Text("Count: $count") // State read in Composition
Box {
count += 1 // State write in Composition (Backwards write!)
Button(onClick = {}) {
count++ // State write in Composition (Backwards write!)
}
}
// [END android_compose_performance_backwards_write_bad_counter]

// [START android_compose_performance_backwards_write_ok_counter]
// Acceptable - but error-prone as someone may add a read before the write : Direct write in Composable body before read
// Prefer writing to state in a callback or SideEffect, or using rememberUpdatedState
@Composable
fun OkCounter() {
var count by remember { mutableIntStateOf(0) }
Button(onClick = {}) {
count++ // State still write in Composition
count++ // State write in Composition
}
Text("Count: $count") // State read in Composition
}
// [END android_compose_performance_backwards_write_ok_counter]
// [END android_compose_performance_backwards_write_read_comp_write_comp]
Loading