Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to scroll screen down automatically when text cursor is out of visible area in android jetpack compose

In Android View System, when cursor(cursor of EditText view in ScrollView) is out of visible area, Android automatically scroll screen to down to make cursor always visible.

EditText in ScrollView

Is there way to achieve this in android Jetpack Compose?

BasicTextField in LazyColumn seems to not support this behavior basically.

BasicTextField in LazyColumn

+)

Here is a composable codes sample that can regenerate this issue.

@Composable
fun TestScreen() {
    val scaffoldState = rememberScaffoldState()
    var content by remember { mutableStateOf("") }

    Scaffold(
        scaffoldState = scaffoldState,
        floatingActionButtonPosition = FabPosition.Center,
    ) {
        val lazyListState = rememberLazyListState()

        LazyColumn(
            state = lazyListState,
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 15.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            item {
                BasicTextField(
                    value = content,
                    onValueChange = { content = it },
                    modifier = Modifier
                        .background(color = Color.Green)
                        .fillMaxWidth()
                        .wrapContentHeight()
                )
            }
        }
    }
}

Activity Declaration in AndroidManifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.android.test">

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.Test">
        <activity
                android:name=".MainActivity"
                android:exported="true"
                android:label="@string/app_name"
                android:theme="@style/Theme.Test"
                android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
like image 519
박찬준 Avatar asked Jan 30 '26 06:01

박찬준


1 Answers

you can create an extension to calculate what line the cursor are by TextFieldValue as above:

val TextFieldValue.selectionLine: Int 
    get() = text.take(selection.start).count { it == '\n' } + 1

and use as following:

val scrollState = rememberScrollState()
var textFieldValue by remember {
    mutableStateOf(value = TextFieldValue())
}

Scaffold(
    modifier = Modifier.fillMaxSize(),
    topBar = { AppBar(onNavigationIconClick = navigateUp) },
) { padding ->
    val textStyle = MaterialTheme.typography.bodyLarge.copy(
        color = MaterialTheme.colorScheme.onBackground,
    )
    val lineHeight = with(LocalDensity.current) {
        textStyle.lineHeight.value.dp.roundToPx()
    }

    LaunchedEffect(key1 = textFieldValue) {
        scrollState.animateScrollTo(value = (textFieldValue.selectionLine - 1) * lineHeight)
    }

    BasicTextField(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(state = scrollState),
        value = textFieldValue,
        onValueChange = { textFieldValue = it },
        textStyle = textStyle,
        decorationBox = { innerTextField ->
            Box(
                modifier = Modifier.padding(paddingValues = padding),
            ) {
                innerTextField()
            }
        }
    )
}
like image 140
Adriano Teles Avatar answered Feb 02 '26 18:02

Adriano Teles