A hardcoded two-pane Row looks fine on a tablet. Put the same screen on a phone in portrait and both panes crush down into slivers nobody can read.
WindowSizeClass hands you the breakpoints. Branch on those instead of guessing at raw dp thresholds.
#The old way
@Composable
fun ProductScreen(product: Product) {
Row {
ProductList(modifier = Modifier.weight(1f))
ProductDetail(product, modifier = Modifier.weight(2f))
}
}
#The new way
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val windowSizeClass = calculateWindowSizeClass(this)
ProductScreen(windowSizeClass = windowSizeClass, product = sampleProduct)
}
}
}
@Composable
fun ProductScreen(windowSizeClass: WindowSizeClass, product: Product) {
when (windowSizeClass.widthSizeClass) {
WindowWidthSizeClass.Compact -> {
ProductDetail(product, modifier = Modifier.fillMaxSize())
}
else -> {
Row {
ProductList(modifier = Modifier.weight(1f))
ProductDetail(product, modifier = Modifier.weight(2f))
}
}
}
}
#Why it matters
- The
Compact/Medium/Expandedbuckets come from breakpoints derived from real device data, not dp cutoffs picked by hand. calculateWindowSizeClassrecomputes on configuration change. Rotate a foldable or resize a multi-window app and the layout reflows on its own.- One activity serves phone, unfolded foldable, and tablet. No separate layout resource sets.
#Gotcha
calculateWindowSizeClass needs an Activity. It reads the current window metrics directly, which rules out calling it from an arbitrary composable deep in the tree.
Compute it once near the root. Pass the WindowSizeClass down as a parameter rather than calling it from every screen.