Loïc Jackotin
Loïc Jackotin

Reputation: 593

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.

Upvotes: 6

Views: 7918

Answers (3)

Vidyesh Churi
Vidyesh Churi

Reputation: 2589

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

Upvotes: 3

Johann
Johann

Reputation: 29877

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.

Upvotes: 0

Yasan Glass
Yasan Glass

Reputation: 1314

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.

Upvotes: 0

Related Questions