Skip to content

Instantly share code, notes, and snippets.

@JH108
Created December 4, 2021 01:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JH108/143964b2ee9fc2498e429b19a954daf7 to your computer and use it in GitHub Desktop.
Save JH108/143964b2ee9fc2498e429b19a954daf7 to your computer and use it in GitHub Desktop.
AutoScroll TextField Compose
package com.me.app
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
/**
* This text field will automatically scroll as far up the screen as possible when selected.
* It completely wraps [TextField]
*/
@Composable
fun AutoScrollingTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape =
MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
) {
val scrollState = LocalFormScrollState.current
val formOffset = LocalFormRootOffset.current
val scope = rememberCoroutineScope()
var scrollToPosition by remember { mutableStateOf(0f) }
TextField(
value = value,
onValueChange = onValueChange,
modifier = modifier
.onGloballyPositioned { coordinates ->
val rootOffset = coordinates.positionInRoot()
scrollToPosition = rootOffset.y - formOffset.y
}
.onFocusChanged {
if (it.isFocused || it.hasFocus) {
scope.launch {
scrollState.animateScrollTo(scrollToPosition.roundToInt())
}
}
},
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle,
label = label,
placeholder = placeholder,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
isError = isError,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = singleLine,
maxLines = maxLines,
interactionSource = interactionSource,
shape = shape,
colors = colors
)
}
package com.me.app
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.unit.dp
import com.eogresources.app.ui.theme.AppTheme
val LocalFormScrollState = compositionLocalOf { ScrollState(0) }
val LocalFormRootOffset = compositionLocalOf { Offset(0f, 0f) }
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Scaffold(
topBar = {
TopAppBar() {
Text("Hello")
IconButton(onClick = { println("Clicked") }) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Favorite"
)
}
}
}
) {
val scrollState = rememberScrollState()
var offset by remember { mutableStateOf(Offset(0f, 0f)) }
CompositionLocalProvider(
LocalFormScrollState provides scrollState,
LocalFormRootOffset provides offset
) {
Column(modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(scrollState)
.onGloballyPositioned { layoutCoordinates ->
offset = layoutCoordinates.positionInRoot()
},
horizontalAlignment = Alignment.CenterHorizontally
) {
listOf(
"Hey",
"Hi",
"Hello",
"Goodbye",
"Again",
"Thing One",
"Thing Two",
"Thing Three"
).forEach { title ->
var value by remember { mutableStateOf("") }
AutoScrollingTextField(
modifier = Modifier.padding(12.dp),
label = { Text(title) },
value = value,
onValueChange = { value = it }
)
}
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment