Hi, what is the correct combination of settings su...
# multiplatform
i
Hi, what is the correct combination of settings such that keyboard opening: 1) keeps text field directly above keyboard 2) doesn't move the top bar out of the screen in a
Scaffold
? (and which also works with a bottom bar, and for Android / iOS) I'm trying using e.g. the Scaffold's
innerPadding
or applying
imePadding()
to the containing column, but there's always some issue, like a gap appearing between input and keyboard, topbar moved out of screen..
Copy code
@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,
    ) { }
}
Here's also a minimal project with this code: https://github.com/ivnsch/textfieldpadding/tree/6717e4d247970316bcc36d52427de408514cff83
j
To satisfy #2, you need to use IME padding to effectively resize your content, rather than pushing it up. I'm not sure if Compose handles scrolling for you beyond "the focused thing is outside of the window", so you might need to manually scroll your list 🤔
i
Scrolling what list? In my actual project there's a lazy list inside of the
weight(1f)
box (replacing
Text("Main content area")
). The scrolling there works fine and seems unrelated to the topbar.
j
I assumed your text field was in some sort of scrollable container, my bad
i
I've not an outer scrollable container. Should I've one? seems like it could possibly conflict with the actual scrollable element which should be inside of the box.
j
In your example above - I'd expect just adding ime padding to the box that contains the text field to achieve what you want. I guess the actual result is your second screenshot, where there's extra space at the bottom?
i
that pushes the topbar out of the screen (last screenshot)
Copy code
Box(
    modifier = Modifier
        .weight(1f)
        .fillMaxWidth()
        .imePadding(),
    contentAlignment = Alignment.Center
) {
    Text("Main content area")
}
j
That's weird, but also isn't that the wrong box? That box contains the Text, not the text field 🤔
i
Ah! if I add the
imePadding
to the text field or a new box containing the text field, the text field effectively disappears when opening the keyboard (and the top bar is still pushed out)
Copy code
Box(
        modifier = Modifier
            .height(60.dp)
            .fillMaxWidth()
            .imePadding()
            .background(Color.Yellow),
    ) {

        BasicTextField(
            value = textState,
            onValueChange = { textState = it },
            modifier = Modifier
//                .height(60.dp)
                .fillMaxSize()
        )
    }
j
I definitely was not expecting that 😂 I don't suppose the other Box with the weight on it is messing things up? What happens if you remove that one and just align the text field to the bottom?
i
like this?
Copy code
private fun Contents() {
    val focusManager = LocalFocusManager.current

    Box(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectTapGestures(onTap = {
                    focusManager.clearFocus()
                })
            }
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize().imePadding(),
            contentAlignment = Alignment.Center
        ) {
            Text("Main content area")
        }

        BasicText(
            modifier = Modifier
                .align(Alignment.BottomCenter)
//                .imePadding()
        )
    }
}

@Composable
private fun BasicText(modifier: Modifier) {
    var textState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
        mutableStateOf(TextFieldValue("..."))
    }

    BasicTextField(
        value = textState,
        onValueChange = { textState = it },
        modifier = modifier
            .height(60.dp)
            .fillMaxWidth()
            .background(Color.Yellow),
    )
}
that still pushes the top bar out (with or without imePadding on the box)
j
You're testing on Android right? What is your
windowSoftInputMode
set to?
(it should be
adjustResize
)
i
Right now running these attempts on Android but original report is about Android & iOS. Tried anyway to set
windowSoftInputMode
to
adjustResize
in the manifest and still looks the same (top bar pushed out)
I was just playing with Jetpack's Jetchat example (Android-only, seeing that I get at least that right) https://github.com/android/compose-samples/tree/main/Jetchat, and it does the right thing until I add a bottom bar. Then it shows a gap between the input and the keyboard with the apparent height of the bottom bar (which is not the bottom bar as it's colored differently)
Copy code
...
bottomBar = { TabsBar() },

@Composable
fun TabsBar() {
    NavigationBar(
        modifier = Modifier.height(100.dp),
        containerColor = Color.Green,
    ) { }
}