Software keyboard overlaps content of jetpack compose view
Asked Answered
C

11

62

Suppose I have some activity with a jetpack-compose content

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ScrollableColumn(
                modifier = Modifier
                    .fillMaxSize()
                    .border(4.dp, Color.Red)
            ) {
                val (text, setText) = remember { mutableStateOf("") }

                TextField(
                    value = text,
                    onValueChange = setText,
                    label = {},
                    modifier = Modifier
                        .fillMaxWidth()
                )

                for (i in 0..100) {
                    Text("Item #$i")
                }
            }
        }
    }

}

If I were to launch this activity and focus on the TextField a software keyboard would pop up.

The interface, however, would not react to it. ScrollableColumn's bottom border (.border(4.dp, Color.Red)) would not be visible, as well as 100th item (Text("Item #$i")).

In other words, software keyboard overlaps content.

How can I make jetpack compose respect visible area changes (due to software keyboard)?

Cataldo answered 24/9, 2020 at 16:15 Comment(0)
I
37

You can use the standard android procedure, but I don't know if a Compose specific way exists.

If you set the SoftInputMode to SOFT_INPUT_ADJUST_RESIZE, the Layout will resize on keyboard change.

class YourActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        
        setContent { /* Composable Content */ }
    }
}

otherwise, you could use the flags in the manifest. See here for more information: Move layouts up when soft keyboard is shown?

Iridosmine answered 24/9, 2020 at 20:23 Comment(15)
It did the trick, android:windowSoftInputMode="adjustResize" (that which you hinted at) works as well. Thank you very much!Cataldo
Setting the flag in the manifest is the same as setting it in code just your preference I guess.Iridosmine
I think this only set bottom inset when keyboard popup, but the content could still not fully visible after keyboard popup, unless ScrollableColumn would keep the focus item scrolling to visible position.Hellhound
thanks but on keyboard ime next button click, the text field still not visible, ie the scrollable column not scrolled, any idea how to solve this ?Snavely
@Snavely I think you have to scroll manually, you can calculate the position by measuring everything in the Column and then set the scroll state to the relative position of the focused element.Iridosmine
@Iridosmine first thanks a lot for your response, yeah this is what I did to solve this using 'scrollState.smoothScrollTo(i * viewSize)' but I was asking if there is any cleaner waySnavely
@Iridosmine do you have a code snippet to know how to perform this calculation ?Pianoforte
@OscarIvan If you have a fixed height for the items you have to get the index of it multiply the height and then pass it to the remembered state for the list. I am not sure if you need to calculate in pixels or dp. If it is the first case you have to include a calculation including the current density. From the top of my head it's: Letval d = LocalDensity.current then use val px = with(d) { dpValue.toPx() }Iridosmine
@OscarIvan developer.android.com/reference/kotlin/androidx/compose/…Iridosmine
Adding: android:windowSoftInputMode="adjustResize" to your Activity element inside Android Manifest file works the best!Liturgy
SOFT_INPUT_ADJUST_RESIZE is deprecated, any updates on that?Probation
I, personally, use the flag (should not be deprecated) in the manifest or Accompanist's inset library (answer below)Iridosmine
Hello In my case, I have to handle both the scenario in my project: Example: Case 1: Need to resize the screen (AdjustResize) Case 2: No need to resize the screen (AdjustResize) If we give this in the manifest and activity how can I handle these two scenarios in my project: Note: Assume that both are different composes and screens.Breadthways
Deprecated. See https://mcmap.net/q/320329/-software-keyboard-overlaps-content-of-jetpack-compose-viewDirndl
it's not going to work for all layouts, your input field may appear behind keyboard...Ase
S
61

Besides Compose, no external libraries are needed for this now. Set android:windowSoftInputMode=adjustResize on the activity in your manifest file

e.g.

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

    <application>
        <activity
            android:name=".MyActivity"
            android:windowSoftInputMode="adjustResize"/>
    </application>

</manifest>

Then for the composable that you want the UI to react to, you can use Modifier.imePadding() which reacts to IME visibility

Box(Modifier.imePadding()) {
  // content
}
Sedda answered 12/10, 2022 at 3:23 Comment(8)
Hello In my case, I have to handle both the scenario in my project: Example: Case 1: Need to resize the screen (AdjustResize) Case 2: No need to resize the screen (AdjustResize) If we give this in the manifest and activity how can I handle these two scenarios in my project: Note: Assume that both are different composes and screens.Breadthways
I don't think "true" is a possible value for this field (which is misspelled)Excreta
this is the correct answer , no need any more to 3rd library ... tested right now, imePadding do the job very wellHeadband
it means that you may forget about adding .imePadding() for each screens where keyboard can be shown... because with android:windowSoftInputMode="adjustResize" the system doesn't put the content above the keyboard automaticallyAse
also what to do you if some screens may not have scrolling attribute (the content is adaptive)? but we set adjustResize for the whole activity, which means it effects all screens...Ase
the problem that for some screens I need to have android:windowSoftInputMode="adjustResize" behaviour but for some not because it make hide your input field behind keyboard for some designs... I need adjustResize + .imePadding() only for screens like chat list (scrollable) with input field below the screen but for all others keep the default behaviourAse
I think it will not scroll the content to make text field visible automaticallyGoethite
Thanks, i used Just imePadding and didnt worked, and blatantly spend time on finding keyboard height by globalpositioning, view invalidation etc to be added as padding, but just this 1 manifest flag does this for you. Helpful!Goss
I
37

You can use the standard android procedure, but I don't know if a Compose specific way exists.

If you set the SoftInputMode to SOFT_INPUT_ADJUST_RESIZE, the Layout will resize on keyboard change.

class YourActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        
        setContent { /* Composable Content */ }
    }
}

otherwise, you could use the flags in the manifest. See here for more information: Move layouts up when soft keyboard is shown?

Iridosmine answered 24/9, 2020 at 20:23 Comment(15)
It did the trick, android:windowSoftInputMode="adjustResize" (that which you hinted at) works as well. Thank you very much!Cataldo
Setting the flag in the manifest is the same as setting it in code just your preference I guess.Iridosmine
I think this only set bottom inset when keyboard popup, but the content could still not fully visible after keyboard popup, unless ScrollableColumn would keep the focus item scrolling to visible position.Hellhound
thanks but on keyboard ime next button click, the text field still not visible, ie the scrollable column not scrolled, any idea how to solve this ?Snavely
@Snavely I think you have to scroll manually, you can calculate the position by measuring everything in the Column and then set the scroll state to the relative position of the focused element.Iridosmine
@Iridosmine first thanks a lot for your response, yeah this is what I did to solve this using 'scrollState.smoothScrollTo(i * viewSize)' but I was asking if there is any cleaner waySnavely
@Iridosmine do you have a code snippet to know how to perform this calculation ?Pianoforte
@OscarIvan If you have a fixed height for the items you have to get the index of it multiply the height and then pass it to the remembered state for the list. I am not sure if you need to calculate in pixels or dp. If it is the first case you have to include a calculation including the current density. From the top of my head it's: Letval d = LocalDensity.current then use val px = with(d) { dpValue.toPx() }Iridosmine
@OscarIvan developer.android.com/reference/kotlin/androidx/compose/…Iridosmine
Adding: android:windowSoftInputMode="adjustResize" to your Activity element inside Android Manifest file works the best!Liturgy
SOFT_INPUT_ADJUST_RESIZE is deprecated, any updates on that?Probation
I, personally, use the flag (should not be deprecated) in the manifest or Accompanist's inset library (answer below)Iridosmine
Hello In my case, I have to handle both the scenario in my project: Example: Case 1: Need to resize the screen (AdjustResize) Case 2: No need to resize the screen (AdjustResize) If we give this in the manifest and activity how can I handle these two scenarios in my project: Note: Assume that both are different composes and screens.Breadthways
Deprecated. See https://mcmap.net/q/320329/-software-keyboard-overlaps-content-of-jetpack-compose-viewDirndl
it's not going to work for all layouts, your input field may appear behind keyboard...Ase
C
23

You can use Accompanist's inset library https://google.github.io/accompanist/insets

first use ProvideWindowInsets at the root of your composable hierarchy most of the time below your app theme compose and set windowInsetsAnimationsEnabled true

ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
// content  }

The use navigationBarsWithImePadding() modifier on TextField

OutlinedTextField(
// other params,
modifier = Modifier.navigationBarsWithImePadding() )

Finaly make sure to call WindowCompat.setDecorFitsSystemWindows(window, false) from your activity(inside onCreate). If you want Software keyboard on/off to animate set your activity's windowSoftInputMode adjustResize in AndroidManifests

<activity
  android:name=".MyActivity"
  android:windowSoftInputMode="adjustResize">
Catricecatrina answered 16/8, 2021 at 12:21 Comment(5)
I only used ProvideWindowInsets(windowInsetsAnimationsEnabled = true) in a Jetpack compose TextField as explained in the answer and it worked for me.Maye
works very good for me, the first answer was not enough for me since I had something below the text field which I wanted to move above keyboard too.Appal
it's not for sroll layoutsKendrickkendricks
What if in 1 compose screen I want to adjust resize and in another not?Kerbstone
Deprecated. See https://mcmap.net/q/320329/-software-keyboard-overlaps-content-of-jetpack-compose-viewDirndl
G
8

Use the modifier imePadding(). This will allow the specific compositions to adjust themselves when the keyboard pops up. This does not require you to set any flag on the activity

Gillmore answered 11/11, 2022 at 8:1 Comment(1)
imePadding() also requires .fillMaxHeight()Madiemadigan
H
5

I faced the same problem.

Use OnGlobalLayoutListener which will observe the actual IME rect size and will be triggered when the soft keyboard is fully visible.

Worked solution for me:

val bringIntoViewRequester = remember { BringIntoViewRequester() }
val scope = rememberCoroutineScope()
val view = LocalView.current
DisposableEffect(view) {
    val listener = ViewTreeObserver.OnGlobalLayoutListener {
        scope.launch { bringIntoViewRequester.bringIntoView() }
    }
    view.viewTreeObserver.addOnGlobalLayoutListener(listener)
    onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(listener) }
}
TextField(
    modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester),
    ...
)

Origin here

Houdon answered 16/4, 2022 at 18:53 Comment(0)
S
3

Fix: Use Modifier.imePadding() on your main layout.

This adds automatic padding to push your content up, keeping it visible above the keyboard. It's like adding a spacer that grows with the keyboard size.

Example:

LazyColumn(modifier = Modifier.imePadding()) {
    TextField(...)
    
    ...
}
Sodalite answered 15/6, 2024 at 20:17 Comment(0)
R
2

I came up with idea of using accompanist insets library.

Someone could be interested because my approach does not modificate the contents of an application.

I link my post below:

(Compose UI) - Keyboard (IME) overlaps content of app

Ripley answered 5/1, 2022 at 18:9 Comment(0)
T
2

In my case just adding android:windowSoftInputMode="adjustResize" to activity was enough to solve the problem.
It depends on how you build your UI. If yours screen's root is a vertically scrollable container or a Box, the keyboard resize might get managed automatically.

Tamarin answered 16/9, 2022 at 8:9 Comment(0)
R
1
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

Is deprecated now in compose use below way for make screen resizable on keyboard Open.

First, add below code in your manifest (add in all activities if have more activities)

<activity
  android:name=".MyActivity"
  android:windowSoftInputMode="adjustResize"
/>

Now, in your views of screens where you want to resize add below:

Column(modifier = Modifier.imePadding()) {

}

Instead of column you can use Box, Row etc.

Rubel answered 19/3, 2024 at 10:47 Comment(0)
D
0

If you want your TextField scroll up when keyboard appears. The following it work for me. You need to add these

class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    
    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    
    setContent { /* Composable Content */ }
}

And add this to your app/build.gradle

implementation "com.google.accompanist:accompanist-insets-ui:0.24.7-alpha"
Dit answered 30/4, 2022 at 4:59 Comment(0)
J
0

In my edge-to-edge application I just use Spacer(8.dp + Modifier.height(WindowInsets.safeDrawing.asPaddingValues().calculateBottomPadding())) at the bottom of the screen. It adds padding for the navbar, or keyboard if it is open, to ensure that the bottom elements do not overlap.

It should also works in non-edge-to-edge apps.

Jaggery answered 3/3, 2024 at 18:32 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.