Window Size in Jetpack Compose

How to implement responsive UI for larger screens

Stefano Natali
4 min readSep 9, 2023

The increasing importance of considering various screen displays when developing an Android application is becoming undeniable, owing to the widespread usage of Android across multiple devices. In my early development days, it was crucial to consider varying screen sizes and ratios due to device orientation and the vast array of devices available, such as tablets. Now, the landscape has expanded to include foldable devices, desktops, TVs, and practically any device imaginable that could potentially run Android.

Notably, large screens constitute an important and rapidly growing segment of active Android devices.

Consequently, the user interface of our application must be responsive and adaptable to different screen sizes, orientations, and form factors. This adaptability implies that the layout changes based on the screen space available, ranging from minor layout adjustments to fill up space, to completely altering layouts to fit the screen.

Jetpack Compose offers a really smart way to manage different window sizes. However, before diving into the code, it’s important to understand how screens are categorized.

Window size class categorize your app’s available display area as compact, medium, or expanded. Available width and height are classified separately, meaning your app has two window size classes at any given time.

This image is crucial as it enables us to understand how window size class will vary across different screens. For example, a phone can have compact for the width and medium or expanded for the height, which will reverse upon changing the orientation.

Let’s begin coding

Ok, I will demonstrate how I managed various window sizes in a simple app I developed to manage books. Obviously, the same principles can be applied or adapted for different situations.

Given the prevalence of vertical scrolling, the available width is typically more significant than the available height. Therefore, our app’s UI will likely be more influenced by the width window size class.

Like always we must start from the gradle:

implementation(platform("androidx.compose:compose-bom:2023.08.00"))
implementation("androidx.compose.material3:material3-window-size-class")

I prefer (and encourage you) to use the Bill of Materials as it allows you to manage all of your Compose library versions by specifying only the BOM’s version.

In our activity, in the setContent block, we can now call:

setContent {
MyTheme {
val windowSize = calculateWindowSizeClass(this)

...

This variable needs to be passed to your composable functions so that you can use it throughout your app.

In my case, the first use was to decide the type of navigation — using the BottomBar for the compact device and the Rail for others. Obviously, this simple function has the potential to be enhanced to manage the expanded screens with, for example, a PermanentNavigationDrawer.

@Composable
fun MainUIWithNavigation(
snackBarHostState: SnackbarHostState,
windowSize: WindowSizeClass
) {
Scaffold(
bottomBar = {
if (windowSize.widthSizeClass == WindowWidthSizeClass.Compact) {
MBottomBar()
}
},
snackbarHost = {
SnackbarHost(hostState = snackBarHostState)
}
) { innerPadding ->
if (windowSize.widthSizeClass > WindowWidthSizeClass.Compact) {
Row(
Modifier
.fillMaxSize()
.padding(innerPadding)
) {
MRailBar()

MainNavHost(
modifier = Modifier,
windowSize = windowSize
)
}
} else {
MainNavHost(
modifier = Modifier.padding(innerPadding),
windowSize = windowSize
)
}
}
}

The result is rather pleasing:

Upon reviewing the images, it’s apparent that the number of book columns varies depending on the screen. Let’s explore how to develop this feature.

Box(
modifier = Modifier
.fillMaxSize()
.pullRefresh(pullRefreshState),
) {


val columns = when (widthSizeClass) {
WindowWidthSizeClass.Compact -> 1
WindowWidthSizeClass.Medium -> 2
WindowWidthSizeClass.Expanded -> 3
else -> 1
}


LazyVerticalGrid(modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
columns = GridCells.Fixed(columns),
content = {

...

It’s straightforward, you just need to decide the number of columns based on your WindowSizeClass.

The same concepts can be applied to a wide variety of use cases, so don’t limit your imagination.

That’s it for today!

I hope these simple examples help you understand the power of WindowSizeClass and how easy it is to manage different screens.

If you have comments or just want to talk about this, you can find me on Twitter and LinkedIn.

Have a great day!

--

--