Skip to content

Instantly share code, notes, and snippets.

@JGeraldoLima
Created February 8, 2025 02:02
Show Gist options
  • Save JGeraldoLima/4e46081bd38750ac9f23728a6f382b6c to your computer and use it in GitHub Desktop.
Save JGeraldoLima/4e46081bd38750ac9f23728a6f382b6c to your computer and use it in GitHub Desktop.
A JetpackCompose component to provide a field that accepts multiple input types
package me.jgeraldo.compose.ui
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults.TextFieldDecorationBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun <T> AnyTypeTextField(
initialValue: T?,
onUpdateField: (T?) -> Unit,
placeholder: String,
_keyboardType: KeyboardType = KeyboardType.Text,
_visualTransformation: VisualTransformation = VisualTransformation.None
) {
// Create a local state for the TextFieldValue to preserve the selection.
var localTextFieldValue by remember {
mutableStateOf(
when (initialValue) {
null -> TextFieldValue("") // most common initial value
is TextFieldValue -> initialValue
is String -> TextFieldValue(
initialValue,
selection = TextRange(initialValue.length)
)
is Int -> {
val text = initialValue.toString()
TextFieldValue(text, selection = TextRange(text.length))
}
is Float -> {
val text = initialValue.toString()
TextFieldValue(text, selection = TextRange(text.length))
}
is Double -> {
val text = initialValue.toString()
TextFieldValue(text, selection = TextRange(text.length))
}
else -> throw IllegalArgumentException("Unsupported type: ${initialValue!!::class.java}")
}
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.border(
width = 1.dp,
color = Color.Gray,
shape = RoundedCornerShape(6.dp)
)
) {
BasicTextField(
value = localTextFieldValue,
onValueChange = { newValue ->
// Update the local state (which preserves the selection/cursor).
localTextFieldValue = newValue
// Handle type casting based on _keyboardType
val newInputValue = when (_keyboardType) {
KeyboardType.Number -> {
newValue.text.toIntOrNull() as T?
}
KeyboardType.Decimal -> {
newValue.text.toFloatOrNull() as T? ?: newValue.text.toDoubleOrNull() as T?
}
KeyboardType.Text -> {
newValue.text as T
}
else -> {
newValue.text as T
}
}
onUpdateField(newInputValue)
},
modifier = Modifier
.defaultMinSize(minHeight = 40.dp)
.fillMaxWidth(),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.None,
keyboardType = _keyboardType,
),
textStyle = TextStyle(
color = Color.Black,
fontSize = 15.sp,
),
visualTransformation = _visualTransformation,
decorationBox = @Composable { innerTextField ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
TextFieldDecorationBox(
enabled = true,
interactionSource = remember { MutableInteractionSource() },
singleLine = true,
value = localTextFieldValue.text,
visualTransformation = VisualTransformation.None,
innerTextField = innerTextField,
placeholder = { Text(text = placeholder) },
contentPadding = PaddingValues(horizontal = 10.dp)
)
}
}
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment