'Jetpack Compose: What is the best way to support all screen sizes?

I searched on Google multiple ways to support multiple screen sizes on Android with Jetpack compose and I finally found the Google documentation: https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes#compose

enum class WindowSizeClass { COMPACT, MEDIUM, EXPANDED }

@Composable
fun Activity.rememberWindowSizeClass() {
    val configuration = LocalConfiguration.current
    val windowMetrics = remember(configuration) {
        WindowMetricsCalculator.getOrCreate()
            .computeCurrentWindowMetrics(this)
    }
    val windowDpSize = with(LocalDensity.current) {
        windowMetrics.bounds.toComposeRect().size.toDpSize()
    }
    val widthWindowSizeClass = when {
        windowDpSize.width < 600.dp -> WindowSizeClass.COMPACT
        windowDpSize.width < 840.dp -> WindowSizeClass.MEDIUM
        else -> WindowSizeClass.EXPANDED
    }

    val heightWindowSizeClass = when {
        windowDpSize.height < 480.dp -> WindowSizeClass.COMPACT
        windowDpSize.height < 900.dp -> WindowSizeClass.MEDIUM
        else -> WindowSizeClass.EXPANDED
    }

    // Use widthWindowSizeClass and heightWindowSizeClass
}

But it might be a problem for ldpi screens and where to store those variables? Do I need to do same as the old way and store dimens value in a dimen folder for all densities? Because for example images in a screen on 400dp might look very big on ldpi screen (~120dp) I'm quite confusing and I'm new to jetpack compose. Thanks in advance for your help.



Solution 1:[1]

You can use library https://github.com/GetStream/butterfly or create

rememberWindowSizeClass.kt

data class WindowSizeClass(
    val widthWindowSizeClass: WindowType,
    val heightWindowSizeClass: WindowType,
    val widthWindowDpSize: Dp,
    val heightWindowDpSize: Dp
) {
    sealed class WindowType {
        object COMPACT : WindowType()
        object MEDIUM : WindowType()
        object EXPANDED : WindowType()
    }
}

@Composable
fun Activity.rememberWindowSizeClass(): WindowSizeClass {
    val configuration = LocalConfiguration.current
    val windowMetrics = remember(configuration) {
        WindowMetricsCalculator.getOrCreate()
            .computeCurrentWindowMetrics(activity = this)
    }
    val windowDpSize = with(LocalDensity.current) {
        windowMetrics.bounds.toComposeRect().size.toDpSize()
    }
    return WindowSizeClass(
        widthWindowSizeClass = when {
            windowDpSize.width < 0.dp -> throw IllegalArgumentException("Dp value cannot be negative")
            windowDpSize.width < 600.dp -> WindowSizeClass.WindowType.COMPACT
            windowDpSize.width < 840.dp -> WindowSizeClass.WindowType.MEDIUM
            else -> WindowSizeClass.WindowType.EXPANDED
        },
        heightWindowSizeClass = when {
            windowDpSize.height < 0.dp -> throw IllegalArgumentException("Dp value cannot be negative")
            windowDpSize.height < 480.dp -> WindowSizeClass.WindowType.COMPACT
            windowDpSize.height < 900.dp -> WindowSizeClass.WindowType.MEDIUM
            else -> WindowSizeClass.WindowType.EXPANDED
        },
        widthWindowDpSize = windowDpSize.width,
        heightWindowDpSize = windowDpSize.height
    )
}

In

MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val windowSize = rememberWindowSizeClass()
            when (windowSize.widthWindowSizeClass) {
                is WindowSizeClass.WindowType.COMPACT -> {
                    AppScreenCompact()
                }
                is WindowSizeClass.WindowType.MEDIUM -> {
                    AppScreenMedium()
                }
                else -> {
                    AppScreenExpanded()
                }
            }
        }
    }
}

For further info refer to Documentation and Sample

Solution 2:[2]

The code above is just an example. You can create your own code based on this idea to match your needs.

You can use the resources system to save the dimensions but I don't really see a reason to do so. If your image is 200dp, it is meant to be 200dp. DP is dynamic. Yes, 200dp will be larger on a small screen but it should because the user is meant to actually see the content of the image.

Your app layout not breaking on different screen sizes and settings is another issue that isn't really that related to compose in theory. You just need to design your layouts in a way that they are not too sensitive to font/dpi size changes meaning a mild font/dpi size change should not be able to break the layout.

Other than this, you can just write different composables for screen configurations that are too different to work with your standard composable.

Solution 3:[3]

Use Jetmagic. It was designed to treat your composables like resources in the same way the older view system treated your xml layouts. It supports all the configuration qualifiers that are supported by resource folders including the screen size and screen density qualifiers.

Using Jetmagic, your composable will be selected based upon device configurations and reselect a different composable should the configuration change at runtime such as device orientation changes.

The framework includes a demo app and there's a detailed article on Medium about it:

https://github.com/JohannBlake/Jetmagic

Jetmagic is a far superior solution than using the WindowSizeClass.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vidyesh Churi
Solution 2 YASAN
Solution 3 Johann