Jetpack Compose imePadding ведёт себя крайне странно, оставаясь после закрытия клавиатуры и вызывает глюки клавиатуры

Записал видео с этим поведением:

https://www.youtube.com/watch?v=6ZY5zIaK82g

Видео записывал на телефоне с Андроид 13, такое же поведение было на другом телефоне и эмуляторе с Андроид 14

В видео есть сразу несколько багов которые я замечал при использовании imePadding:

  • Отступ часто меньше по высоте. чем сама клавиатура
  • Иногда отступ остаётся на месте, даже если клавиатура закрыта
  • Иногда отступ это просто мелкая красная полоска в нижней части экрана
  • И почти всегда клавиатура показывается ещё раз на секунду, после закрытия. Такое же поведение было и без LazyColumn вместе с adjustPan. Вообще такое поведение встречаю во всех возможных конфигурациях. Это баг compose или я что-то не так делаю?

Мой код:

MainActivity.kt:

package com.samp.gui

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.TextField
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        val windowInsetsController =
            WindowCompat.getInsetsController(window, window.decorView)
        windowInsetsController.systemBarsBehavior =
            WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())

        WindowCompat.setDecorFitsSystemWindows(window, false)

        val composeView = ComposeView(this)

        setContentView(composeView)

        composeView.setContent {
            val scrollState = rememberLazyListState()

            LazyColumn(
                modifier = Modifier
                    .background(Color.Red)
                    .fillMaxSize(),
                state = scrollState
            ) {
                item {
                    Column(
                        modifier = Modifier
                            .fillParentMaxSize()
                            .background(Color.Green)
                    ) {
                        Box(Modifier.fillMaxSize()) {
                            var text by remember {
                                mutableStateOf("")
                            }

                            TextField(
                                modifier = Modifier.align(Alignment.TopCenter),
                                value = text,
                                onValueChange = { text = it },
                                singleLine = true
                            )
                        }
                    }
                }
                item {
                    Spacer(Modifier.imePadding())
                }
            }
        }
    }
}

Я использую AppCompatActivity, потому что моё основное приложение только переходит к compose и в этом тестовом я хочу повторить всё по максимуму. LazyColumn использую для fillParentMaxSize, чтобы первый блок всегда заливал весь экран, а второй был только отступ для клавиатуры. Так можно залить весь фон одним цветом, для красоты, чтобы не было чёрных отступов по бокам клавиатуры. Обычная Column с scrollable не даёт fillParentMaxSize и в ней приложение не заполняет весь экран.

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Gui"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.Gui"
            android:screenOrientation="sensorLandscape"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

С adjustPan вместо adjustResize и без LazyColumn всё работает чуть лучше, но видны чёрные полосы по бокам. При этом баг с показывающейся на секунду клавиатурой остаётся.

themes.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Theme.Gui" parent="@style/Theme.AppCompat.NoActionBar">
        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
    </style>
</resources>

Кроме imePadding я тестил ViewCompat.getRootWindowInsets(view).getInsets(WindowInsetsCompat.Type.ime()) и Modifier.windowInsetsBottomHeight(WindowInsets.ime) все дают такое же поведение, как и на видео


Ответы (0 шт):