Supporting different screen sizes on Android with Jetpack Compose
In this post, we are going to discover the pitfalls of using hard-coded dimensions and a way to support different screen sizes.

Since the early days of Android, it has been recommended to use Density-independent pixel (abbreviated dp or dip) instead of Pixels for creating layouts and UI elements. Dp helps in creating layouts on multiple screen sizes with different densities easier and more manageable.
“Dp is a virtual pixel unit that takes the same amount of space as 1 pixel on 160 dpi (mdpi) screen.” — Android Design Bytes, gift that keeps on giving ❤
That means 50dp will take roughly the same amount of physical real world space on a Pixel 5 and on a Nexus One, roughly the size of a finger tip. But if you think about it, we have much less space on smaller devices (regardless of the density) as they are, as you guessed, smaller. Yet we use the same set of dimensions for those devices as our latest and biggest devices.
The Problem — Same dimensions on Nexus One and Pixel 5

Here we can see that while the screen looks pretty neat on a Pixel 5, it looks quite bad on a smaller device. The padding takes a considerable proportion of the space, the text size is way too big, and there’s not enough space left for the button so it gets cropped. Even though the screenshot on the post doesn’t look too shabby, it leaves a lot to be desired when experienced on an actual device. Obviously we can make the screen scroll-able and that would be fine for most apps but we want to do better, don’t we? 🤓
In the screenshot above, we had been using Dimensions by creating a Dimens object with all the app specific dimensions in one place.
Although this allowed us to define dimensions in Kotlin, we did not take advantage of resource configurations as individual dimensions have the same values for all the screen sizes and configurations.
For instance, Dimens.grid_1 will always be 8.dp regardless of the screen size.
We can take inspiration from the way colors are set in the Theme that allows apps to support multiple themes with relative ease. The idea is to provide values based on a certain configuration, let’s see how colors are setup and how do they update when the theme is updated.
We define two Colors objects LightThemeColors and DarkThemeColors and based on the darkTheme flag one of them is provided to the CompositionLocal.
To use colors in the composable screens reference them through the theme object.
Similarly, we can define dimensions for different configurations and take advantage of smallest width configurations. We can define a default set of dimensions and one for devices with shortest width of at least 360dp.
This is similar to defining two different dimens.xml files in res/values and res-values-sw360dp. This forces us to define all the non default dimensions for different configurations at compile time. With the xml approach, we do not get these checks, easily anyway.
Note that we can also define dimensions with a default value like minimum_touch_target = 48.dp
Now, we have defined both sets of dimensions but how does the system know which one to use? We use CompositionLocal and provide dimensions based on the current configuration.
As you can see, we provide smallDimensions for small devices and sw360Dimensions for devices with screenWidthDp of at least 360. We can also define multiple typography objects with different font sizes in the same way. Dimensions can now be referenced through the theme object as shown below.
And now our screen looks much better on a Nexus One!

If you are migrating the app from the Android View system to Jetpack Compose and don’t want to redefine dimensions again, another approach would be to directly reference the dimensions from xml (using dimensionResource(id=R.dimen.grid_1)) similar to how strings are used in Compose. You would have to define multiple sets of dimensions in res/values and res/values-sw360dp to have the desired result.
In most cases, we can rely on various modifiers like fillMaxWidth, fillMaxHeight, wrapContent or think of our layouts in percentages/fractions or aspect ratios but there are definitely cases where it’s important to use dimensions. Following the same pattern, we can define dimensions or integers for sw600dp (7 inch Tablets) and sw720dp (10 inch Tablets) whenever they make a comeback on Android. 🙃 In many cases the layouts for tablets are different and “traditionally” have resided in res/layout-sw600dp or res/layout-sw720dp or other configuration for even larger devices, but we can still rely on common dimensions being defined for those configurations so we do not duplicate them in all the layout files/composable screens for larger screen sizes.
Migrating existing Dimens
In case you already have Dimens defined in the object like in the beginning of the post, you can use replaceWith param from the Deprecated annotation for IntelliJ to assist with the migration.