Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Software keyboard overlaps content of jetpack compose view

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

like image 479
Nycta Avatar asked Sep 24 '20 16:09

Nycta


People also ask

What is recomposition in jetpack compose?

Recomposition is when Jetpack Compose re-executes the composables that may have changed in response to state changes, and then updates the Composition to reflect any changes. A Composition can only be produced by an initial composition and updated by recomposition.

What is ProvideWindowInsets?

ProvideWindowInsets() {} means you can access the window inset values with LocalWindowInsets. current in any of the child compose functions inside ProvideWindowInsets() {}. With consumeWindowInsets = false, the window insect values keep passing down the view hierarchy.

Is jetpack compose stable now?

Today, we're releasing version 1.2 of Jetpack Compose, Android's modern, native UI toolkit, continuing to build out our roadmap.

Does jetpack compose use views?

Jetpack Compose is designed to work with the established View-based UI approach. If you're building a new app, the best option might be to implement your entire UI with Compose.


3 Answers

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?

like image 167
2jan222 Avatar answered Oct 12 '22 09:10

2jan222


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">
like image 20
mubarak adem Avatar answered Oct 12 '22 07:10

mubarak adem


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

like image 3
Victor Skurchik Avatar answered Oct 12 '22 07:10

Victor Skurchik