Android. Screen size and orientation for Compose layouts

ilushakr
3 min readJun 12, 2023

--

With common xml approach we are used to using qualifiers for our layouts. But what we can do with compose layouts?

What’s about?

In this article I want to share my convenient way to handle screen configuration changes

Let’s write some code. A little bit

Whole our logic are going to fit in one file

Create file DeviceScreenConfiguration.kt, inside declare data class with the same name. Inside this class let’s declare three more enum classes: DeviceScreenOrientation and DeviceScreenSize

data class DeviceScreenConfiguration(
val size: DeviceScreenSize,
val orientation: DeviceScreenOrientation
) {

enum class DeviceScreenOrientation {
Portrait,
Landscape
}

enum class DeviceScreenSize(val screenWidthRange: IntRange) {
...
}

...
}

If everything is clear with DeviceScreenOrientation class, we need to stop on DeviceScreenSize:

enum class DeviceScreenSize(val screenWidthRange: IntRange) {
Small(0 until 600),
Medium(600 until 720),
Big(720 until Int.MAX_VALUE);

var actualWidth: Int = -1
private set

companion object {
fun getScreenSize(height: Int, width: Int): DeviceScreenSize {
val actualWidth = minOf(height, width)

return values().firstOrNull {
it.screenWidthRange.contains(actualWidth)
}?.apply DeviceScreenSize@{
this@DeviceScreenSize.actualWidth = actualWidth
} ?: throw IllegalScreenSize()

}

private const val illegalScreenSizeMessage = "Screen size can not be less than 0"

class IllegalScreenSize : Throwable(illegalScreenSizeMessage)

}
}

For our example we have three sizes: Small — for handheld, Medium — for foldables and small tablets and Big — for big tablets. As parameter we declare ranges for our enums (for handhelds from 0 to 599, etc). Variable “actualWidth” will be actualized when we find suitable type for given width in Dp with function “getScreenSize”.

We also have a few more methods, one field and one enum class Config inside DeviceScreenOrientation class:

enum class Config {
SmallPortrait,
MediumPortrait,
BigPortrait,
SmallLandscape,
MediumLandscape,
BigLandscape
}

val config: Config
get() = when (orientation) {
DeviceScreenOrientation.Landscape -> {
when (size) {
DeviceScreenSize.Big -> Config.BigLandscape
DeviceScreenSize.Medium -> Config.MediumLandscape
DeviceScreenSize.Small -> Config.SmallLandscape
}
}
DeviceScreenOrientation.Portrait -> {
when (size) {
DeviceScreenSize.Big -> Config.BigPortrait
DeviceScreenSize.Medium -> Config.MediumPortrait
DeviceScreenSize.Small -> Config.SmallPortrait
}
}
}

val isMobile: Boolean
get() = this.size == DeviceScreenSize.Small

val isTablet: Boolean
get() = !isMobile

These only for easy interaction with screen config.

Config class declares variations for orientation mixed with size.

Now let’s write composable function to handling screen changes

@Composable
fun rememberScreenConfiguration(): DeviceScreenConfiguration {
val configuration = LocalConfiguration.current

val screenWidth by remember(key1 = configuration.screenWidthDp) {
mutableStateOf(configuration.screenWidthDp)
}
val screenHeight by remember(key1 = configuration.screenHeightDp) {
mutableStateOf(configuration.screenHeightDp)
}

val screenOrientation by remember(key1 = configuration.orientation) {
val orientation = when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
DeviceScreenOrientation.Landscape
}
else -> {
DeviceScreenOrientation.Portrait
}
}
mutableStateOf(orientation)
}

return DeviceScreenConfiguration(
size = DeviceScreenSize.getScreenSize(screenHeight, screenWidth),
orientation = screenOrientation
)
}

as you can see, we use “remember” function with key to indicate what changes we want to catch and handle.

Result

Now we can use this compose function in or project to customize screens for different sizes and orientation by calling it

Easy

You can check full code on my Github repo

--

--

ilushakr
ilushakr

Written by ilushakr

0 Followers

Android dev

No responses yet