7

Since we have differen screen sizes and resolutions, I would like to place a Composable on top of unfolded keybard:

enter image description here

The keyboard (see above picture) is visible and I want to show nother composable (red square) like a message for a few seconds..

Using Jetpack Compose what would be a easy way to position that red composable?

Ralf Wickum
  • 2,850
  • 9
  • 55
  • 103

2 Answers2

5

This question can be solved using WindowInsets.isImeVisible however for it to return correct values you should set

    WindowCompat.setDecorFitsSystemWindows(window, false)

i set this in Activity before setContent{}

Using WindowInsets.isImeVisible to check whether keyboard is open or not.

And we need to show message while keyboard is opening up so

val offsetY = WindowInsets.ime.getBottom(density)
var previousOffset by remember { mutableStateOf(0) }

val isKeyboardGoingDown by remember(offsetY) {
    derivedStateOf {
        val isGoingDown = previousOffset - offsetY > 0
        previousOffset = offsetY
        isGoingDown
    }
}

is used for tracking whether keyboard is going up or down with LaunchedEffect

LaunchedEffect(key1 = isImeVisible, key2 = isKeyboardGoingDown) {

    if (isImeVisible && !isKeyboardGoingDown) {
        showMessage = true
        delay(1000)
        showMessage = false
    } else {
        showMessage = false

    }
}

Full implementation

@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun TimedMessageLayout() {

    val isImeVisible = WindowInsets.isImeVisible
    var showMessage by remember { mutableStateOf(false) }
    val density = LocalDensity.current
    val offsetY = WindowInsets.ime.getBottom(density)
    var previousOffset by remember { mutableStateOf(0) }
    
    val isKeyboardGoingDown by remember(offsetY) {
        derivedStateOf {
            val isGoingDown = previousOffset - offsetY > 0
            previousOffset = offsetY
            isGoingDown
        }
    }

    LaunchedEffect(key1 = isImeVisible, key2 = isKeyboardGoingDown) {

        if (isImeVisible && !isKeyboardGoingDown) {
            showMessage = true
            delay(1000)
            showMessage = false
        } else {
            showMessage = false

        }
    }

    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomStart) {

        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(WindowInsets.systemBars.asPaddingValues())
                .border(2.dp, Color.Green)
        ) {
            Image(
                modifier = Modifier
                    .fillMaxWidth()
                    .aspectRatio(4 / 3f),
                painter = painterResource(id = R.drawable.landscape1),
                contentDescription = null
            )

            var text by remember { mutableStateOf("") }

            Text(
                "Ime visible: ${WindowInsets.isImeVisible}, isKeyboardGoingDown: $isKeyboardGoingDown\n" +
                        "ime bottom: ${WindowInsets.ime.getBottom(density)}\n"
            )
            Spacer(modifier = Modifier.weight(1f))
            TextField(
                value = text,
                onValueChange = { text = it }
            )
        }

        if (showMessage && !isKeyboardGoingDown && offsetY != 0) {
            Box(modifier = Modifier
                .offset {
                    IntOffset(0, -offsetY)
                }
                .fillMaxWidth()
                .height(200.dp)
                .border(3.dp, Color.Red))
        }
    }
}

Result

enter image description here

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • val offsetY = WindowInsets.ime.getTop(LocalDensity.current) is 0 always – Ralf Wickum Aug 23 '22 at 08:37
  • I know. But i found why it returns 0 all the time. I answered it [here](https://stackoverflow.com/questions/73455192/android-jetpack-compose-statusbar-height/73455592#73455592). i will check out your question with correct values now – Thracian Aug 23 '22 at 08:42
  • @RalfWickum have you checked updated answer? It goes up when keyboard is going up and disappears specified time – Thracian Sep 14 '22 at 09:27
2

Try this solution from here, I slightly modified

@Composable
fun keyboardHeightAsState(): State<Int> {
    val keyboardHeight = remember { mutableStateOf(0) }
    val view = LocalView.current
    DisposableEffect(view) {
        val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
            val rect = Rect()
            view.getWindowVisibleDisplayFrame(rect)
            val screenHeight = view.rootView.height
            val keypadHeight = screenHeight - rect.bottom
            keyboardHeight.value = if (keypadHeight > screenHeight * 0.15) {
                keypadHeight
            } else {
                0
            }
        }
    }

    return keyboardHeight
}

But I still have a small difference. I guess its the top bar, which I have to subtract

Mr.T
  • 72
  • 6