24

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)?

Nycta
  • 597
  • 2
  • 4
  • 10

5 Answers5

21

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?

2jan222
  • 1,302
  • 2
  • 11
  • 25
  • 4
    It did the trick, `android:windowSoftInputMode="adjustResize"` (that which you hinted at) works as well. Thank you very much! – Nycta Sep 25 '20 at 02:44
  • Setting the flag in the manifest is the same as setting it in code just your preference I guess. – 2jan222 Sep 25 '20 at 10:10
  • 3
    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. – Yu-Hsuan Dec 19 '20 at 15:53
  • 4
    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 ? – Hoby Jan 25 '21 at 19:20
  • @Hoby 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. – 2jan222 Jan 25 '21 at 20:25
  • @2jan222 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 way – Hoby Jan 25 '21 at 21:03
  • @2jan222 do you have a code snippet to know how to perform this calculation ? – Oscar Ivan Sep 02 '21 at 21:49
  • @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: Let``val d = LocalDensity.current`` then use ``val px = with(d) { dpValue.toPx() }`` – 2jan222 Sep 02 '21 at 21:55
  • @OscarIvan https://developer.android.com/reference/kotlin/androidx/compose/foundation/ScrollState#animateScrollTo(kotlin.Int,androidx.compose.animation.core.AnimationSpec) – 2jan222 Sep 02 '21 at 21:59
  • Adding: android:windowSoftInputMode="adjustResize" to your Activity element inside Android Manifest file works the best! – Hassa Feb 17 '22 at 18:46
  • SOFT_INPUT_ADJUST_RESIZE is deprecated, any updates on that? – barryalan2633 Apr 08 '22 at 23:28
  • I, personally, use the flag (should not be deprecated) in the manifest or Accompanist's inset library (answer below) – 2jan222 Apr 10 '22 at 12:15
18

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">
mubarak adem
  • 204
  • 1
  • 4
1

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

Drogheda
  • 150
  • 3
  • 12
1

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

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"
Nak Vanna
  • 9
  • 2