I'm developing a typical chat screen, with the text input at the bottom. The app has a bottom bar with tabs and a topbar.
I'm struggling at getting what I understand to be the expected behavior (Android, iOS):
- When keyboard is opened, text input is (directly) on top of keyboard.
- The topbar doesn't move, i.e. stays at top of screen.
- Bottom bar is visible at bottom of screen when keyboard is closed.
I've tried different combinations of using scaffold's innerPadding
, applying imePadding()
to different elements, or not applying it, etc. There's always a different issue that doesn't fulfill the 3 points above. E.g. topbar is pushed out of screen, there's space with size of tabbar between input and keyboard. Sometimes there are platform specific differences.
Are my expectations correct? if yes, what's wrong with the code? if not, what to change?
Here's a repo with this code: https://github.com/ivnsch/textfieldpadding/tree/6717e4d247970316bcc36d52427de408514cff83
Issue example when opening keyboard (current code):
Commenting imePadding()
, space goes away, now topbar is pushed out:
package foo.bar.inputtestdemo
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
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.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.ui.tooling.preview.Preview
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun App() {
Scaffold(
topBar = {
Column {
TopAppBar(title = { Text("topbar") })
}
},
bottomBar = { TabsBar() }
) { innerPadding ->
// Box(modifier = Modifier.fillMaxSize()) {
Box(modifier = Modifier.padding(innerPadding)) {
// Box {
Contents()
}
}
}
@Composable
private fun Contents() {
val focusManager = LocalFocusManager.current
Box {
Column(
modifier = Modifier
.imePadding()
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures(onTap = {
focusManager.clearFocus()
})
},
) {
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text("Main content area")
}
BasicText()
}
}
}
@Composable
private fun BasicText() {
var textState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue("..."))
}
BasicTextField(
value = textState,
onValueChange = { textState = it },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.background(Color.Yellow),
)
}
@Composable
fun TabsBar() {
NavigationBar(
modifier = Modifier.height(100.dp),
containerColor = Color.Green,
) { }
}