14

How can I get the screen size in Compose Multiplatform without using expect/actual?

I have found the following explanation in https://github.com/JetBrains/compose-multiplatform/discussions/3225#discussioncomment-7195192:

You can also use a Layout as your root Composable and obtain the size from the passed constraints. This solution doesn't depend on platform implementations and also gives you the exact size given to your Composable which could be different from the device screen size in some cases.

Once you have that size can use a CompositionLocal to pass it down the composable tree

I have not been able to fully understand or successfully implement the suggested layout.

5 Answers 5

9

Starting with Multiplatform Compose version 1.5.10-beta01 (https://github.com/JetBrains/compose-multiplatform/releases/tag/v1.5.10-beta01) you can use LocalWindowInfo.current.containerSize for non-android targets.

Android has the usual LocalConfiguration.current, so you can use that there.

Sign up to request clarification or add additional context in comments.

4 Comments

I'm getting this error on desktop/Windows, Exception in thread "main" java.lang.IllegalStateException: CompositionLocal LocalWindowInfo not present. I'm using compose version 1.6.10.
@Yamin see the issue here stackoverflow.com/questions/78490378/…. It works for me on the Compose version 1.7.0-alpha03.
Right, I used another solution BTW. stackoverflow.com/a/75368712/15055605
I use the Toolkit myself as well, but for defining the size of the Window, which then, in turn, propagates into the LocalWindowInfo.current.containerSize. Multiplatform is about having a single solution, this is exactly it.
9

This implementation worked for me:

In commonMain:

@Composable
expect fun getScreenWidth(): Dp

@Composable
expect fun getScreenHeight(): Dp

In androidMain:

@Composable
actual fun getScreenWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp

@Composable
actual fun getScreenHeight(): Dp = LocalConfiguration.current.screenHeightDp.dp

In iosMain:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
actual fun getScreenWidth(): Dp = LocalWindowInfo.current.containerSize.width.pxToPoint().dp

@OptIn(ExperimentalComposeUiApi::class)
@Composable
actual fun getScreenHeight(): Dp = LocalWindowInfo.current.containerSize.height.pxToPoint().dp

fun Int.pxToPoint(): Double = this.toDouble() / UIScreen.mainScreen.scale

2 Comments

would not it convert dp value as dp twice?
@GeorgeShalvashvili No
8

I got the answer at https://github.com/JetBrains/compose-multiplatform/discussions/3225#discussioncomment-7386013 🙌

Using that I've created a small example that might be helpful for others looking for the answer:

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout

@Composable
fun App() {
    val screenSize = remember { mutableStateOf(Pair(-1, -1)) }
    Layout(
        content = {
            Box(modifier = Modifier.fillMaxSize()) {
                Text("Screen size: ${screenSize.value.first}x${screenSize.value.second}px", modifier = Modifier.align(Alignment.Center))
            }
        },
        measurePolicy = { measurables, constraints ->
            // Use the max width and height from the constraints
            val width = constraints.maxWidth
            val height = constraints.maxHeight

            screenSize.value = Pair(width, height)
            println("Width: $width, height: $height")

            // Measure and place children composables
            val placeables = measurables.map { measurable ->
                measurable.measure(constraints)
            }

            layout(width, height) {
                var yPosition = 0
                placeables.forEach { placeable ->
                    placeable.placeRelative(x = 0, y = yPosition)
                    yPosition += placeable.height
                }
            }
        }
    )
}

Here's a screenshot of the code running on macOS, iOS simulator and Android emulator: enter image description here

Comments

2

How to Get Screen Width and Height in Compose Multiplatform (Android & iOS)

I found a solution that works consistently across both Android and iOS in a Compose Multiplatform project. Below is the exact same approach implemented for both platforms.


For Android:

We use LocalConfiguration to get the screen dimensions in dp and convert them to pixels using LocalDensity.

@Composable
actual fun getScreenHeight(): Float {
    val configuration = LocalConfiguration.current
    return with(LocalDensity.current) { configuration.screenHeightDp.dp.toPx() }
}

@Composable
actual fun getScreenWidth(): Float {
    val configuration = LocalConfiguration.current
    return with(LocalDensity.current) { configuration.screenWidthDp.dp.toPx() }
}


for Ios 

@OptIn(ExperimentalComposeUiApi::class)
@Composable
actual fun getScreenWidth(): Float = with(LocalDensity.current) { 
    LocalWindowInfo.current.containerSize.width.toDp().toPx() 
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
actual fun getScreenHeight(): Float = with(LocalDensity.current) { 
    LocalWindowInfo.current.containerSize.height.toDp().toPx() 
}

Comments

0

I would go for the following approach

in the common code

data class ScreenSize(
  val width:Dp,
  val height:Dp,
)

@Composable
expect fun getScreenSize(): ScreenSize


val ScreenSizeValue: ScreenSize
@Composable
get() = getScreenSize()

in androidMain

@Composable
actual fun getScreenSize() = ScreenSize(
    width = LocalConfiguration.current.screenWidthDp.dp,
    height = LocalConfiguration.current.screenHeightDp.dp
)

in IosMain

@Composable
actual fun getScreenSize() = remember {
    val screen = UIScreen.mainScreen
    ScreenSize(
        width = CGRectGetWidth(screen.bounds).dp,
        height = CGRectGetHeight(screen.bounds).dp
    )
}

now in common:

val screenSize = ScreenSizeValue
// or
val screenSize = getScreenSize()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.