Laraib07
Laraib07

Reputation: 31

How to keep cursor in view in jetpack compose TextField?

I am working on a jetpack compose notes app. I used two textfield inside a column but the cursor keeps going behind the keyboard. I also checked official android architecture example apps. They also used some hack to keep the cursor in view.

Is there any solution.

Video: https://imgur.com/a/LCLwe7p Github: https://github.com/laraib07/zenote/blob/main/app/src/main/java/com/laraib07/zenote/ui/screen/add_edit/AddEditNote.kt

The cursor must remain on screen.

Upvotes: 2

Views: 515

Answers (1)

mcscoder
mcscoder

Reputation: 41

you can try these steps:

  1. Prevent keyboard overlapping content by setting android:windowSoftInputMode=adjustResize on the activity in your manifest file. For example:
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
  1. To keep the cursor always visible while typing you can use BringIntoViewRequester() to scroll the container to the position where the cursor is presenting. Here is an example:
val bringIntoViewRequester = remember { BringIntoViewRequester() }
val coroutineScope = rememberCoroutineScope()

var textFieldValue by remember { mutableStateOf(TextFieldValue()) }

BasicTextField(
    value = textFieldValue,
    onValueChange = { textFieldValue = it },
    textStyle = TextStyle(fontSize = 30.sp),
    onTextLayout = {
        val cursorRect = it.getCursorRect(textFieldValue.selection.start)
        coroutineScope.launch {
            bringIntoViewRequester.bringIntoView(cursorRect)
        }
    },
    modifier = Modifier
        .bringIntoViewRequester(bringIntoViewRequester)
        .fillMaxWidth()
)

Here's an example with two text fields inside a vertically scrollable column:

val bringIntoViewRequester1 = remember { BringIntoViewRequester() } // Text Field 1 BringIntoViewRequester()
val bringIntoViewRequester2 = remember { BringIntoViewRequester() } // Text Field 2 BringIntoViewRequester()
val coroutineScope = rememberCoroutineScope()

var textFieldValue1 by remember { mutableStateOf(TextFieldValue()) } // Text Field 1 value
var textFieldValue2 by remember { mutableStateOf(TextFieldValue()) } // Text Field 2 value

Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(10.dp)
        .border(1.dp, Color.Red, RectangleShape)
        .verticalScroll(rememberScrollState())
) {
    Surface(modifier = Modifier.border(1.dp, Color.Black, RectangleShape)) {
        BasicTextField(
            value = textFieldValue1,
            onValueChange = { textFieldValue1 = it },
            textStyle = TextStyle(fontSize = 30.sp),
            onTextLayout = {
                var cursorRect = it.getCursorRect(textFieldValue1.selection.start) // Get Text Field 1 cursor position
                coroutineScope.launch {
                    bringIntoViewRequester1.bringIntoView(cursorRect) // Scroll to Text Field 1 cursor position
                }
            },
            modifier = Modifier
                .bringIntoViewRequester(bringIntoViewRequester1) // Add BringIntoViewRequester() modifier for Text Field 1
                .fillMaxWidth()
        )
    }
    Surface(modifier = Modifier.border(1.dp, Color.Black, RectangleShape)) {
        BasicTextField(
            value = textFieldValue2,
            onValueChange = { textFieldValue2 = it },
            textStyle = TextStyle(fontSize = 30.sp),
            onTextLayout = {
                var cursorRect = it.getCursorRect(textFieldValue2.selection.start) // Get Text Field 2 cursor position
                coroutineScope.launch {
                    bringIntoViewRequester2.bringIntoView(cursorRect) // Scroll to Text Field 2 cursor position
                }
            },
            modifier = Modifier
                .bringIntoViewRequester(bringIntoViewRequester2) // Add BringIntoViewRequester() modifier for Text Field 2
                .fillMaxWidth()
        )
    }
}

Upvotes: 1

Related Questions