Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

visibility of a part of screen when keyboard is open in jetpack compose

I have code like this and I want the button on the bottom to be visible when keyboard is opened to edit the textfield.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp)
                    .verticalScroll(rememberScrollState())
                    .imePadding(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "LARGE TITLE", fontSize = 32.sp, fontWeight = FontWeight.Bold)
                Spacer(modifier = Modifier.height(400.dp))
                OutlinedTextField(value = "Sample Text", onValueChange = {})
                Spacer(modifier = Modifier.weight(1f))
                Button(onClick = { /*TODO*/ }) {
                    Text(text = "Sample Button")
                }
            }
        }
    }

I also have put android:windowSoftInputMode="adjustResize" in manifest. I achieved my purpose by adding reverseScrolling = true (.verticalScroll(rememberScrollState(), reverseScrolling = true)) to the column modifier with help of an answer of @Halifax to this. I'm wondering how this has solved the issue exactly. Can I use this approach in similar views? I'm worried that it might cause some weird behavior if the screen got more complicated. without reverseScrolling = true

with reverseScrolling = true

like image 836
Ahmadabadiha Avatar asked Nov 02 '25 03:11

Ahmadabadiha


2 Answers

Here is a slightly different solution that keeps the button in the column. It uses the BringIntoViewRequester to scroll the button into view whenever the size of the column is changed.

    setContent {
        val bringIntoViewRequester = remember { BringIntoViewRequester() }
        val coroutineScope = rememberCoroutineScope()
        val bringButtonIntoView: () -> Unit = {
            coroutineScope.launch {
                delay(1)
                bringIntoViewRequester.bringIntoView()
            }
        }
        Box(modifier = Modifier.imePadding()) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp)
                    .verticalScroll(rememberScrollState())
                    .onSizeChanged { bringButtonIntoView() },
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "LARGE TITLE", fontSize = 32.sp, fontWeight = FontWeight.Bold)
                Spacer(modifier = Modifier.height(400.dp))
                OutlinedTextField(value = "Sample Text", onValueChange = {})
                Spacer(modifier = Modifier.weight(1f))
                Button(
                    onClick = { /*TODO*/ },
                    modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester),
                ) {
                    Text(text = "Sample Button")
                }
            }
        }
    }
like image 58
Jack Avatar answered Nov 03 '25 18:11

Jack


The reverseScrolling = true attribute reverses the scroll direction, similar to chat screens in messaging apps. This means the content starts from the bottom instead of the top. Consequently, the Button at the bottom is displayed above the keyboard.

However, if your content is large enough to be scrollable, such as when you have multiple OutlinedTextField elements, you may encounter the following issues:

  • When the user enters the screen, the column will start displaying content from the bottom due to the reversed scroll.
  • As you scroll the content and edit other text fields, the Button will not remain visible above the keyboard.

This setup only works when the content fits within the window and is not scrollable. If your content isn't scrollable, you can simply remove the scroll from the Column, and your problem will be resolved.

If your content is large and your Column is scrollable, you can solve this problem by placing the scrollable content in a Column with the weight(1f) modifier. Then, wrap it in a non-scrollable Column with the fillMaxSize() modifier, along with a Button that remains visible when the keyboard opens at the bottom.

First, ensure that the activity's windowSoftInputMode is set to adjustResize:

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

Next, structure your content as follows:

Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
    horizontalAlignment = Alignment.CenterHorizontally
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .weight(1f)
            .verticalScroll(rememberScrollState()),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "LARGE TITLE", fontSize = 32.sp, fontWeight = FontWeight.Bold)
        Spacer(modifier = Modifier.height(400.dp))
        OutlinedTextField(value = "Sample Text", onValueChange = {})
        Spacer(modifier = Modifier.height(400.dp))
        OutlinedTextField(value = "Sample Text", onValueChange = {})
        Spacer(modifier = Modifier.height(400.dp))
        OutlinedTextField(value = "Sample Text", onValueChange = {})
        Spacer(modifier = Modifier.height(400.dp))
    }
    Button(onClick = { /*TODO*/ }) {
        Text(text = "Sample Button")
    }
}

This approach ensures that your button remains visible above the keyboard while allowing the content to be scrollable.

like image 42
Adnan Habib Avatar answered Nov 03 '25 18:11

Adnan Habib