← All shorts
30Compose Material3 1.1+2 min read

WindowSizeClass for Adaptive Layouts

Branch layout structure on coarse width/height buckets instead of hardcoding phone-only dimensions.

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/Expanded buckets come from breakpoints derived from real device data, not dp cutoffs picked by hand.
  • calculateWindowSizeClass recomputes 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.

androidcomposeadaptive

Related shorts