Is it possible to use the BasicTextField without the system's keyboard showing when it is on focus (I have my own custom keyboard onscreen)?
I tried setting the readOnly = true. The system's keyboard is not shown, but the blinking cursor isn't either.
The key to achieving this is to use LocalSoftwareKeyboardController and FocusRequester in combination.
First, focus on the TextField using FocusRequester and then hide the soft keyboard.
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember {
FocusRequester()
}
LaunchedEffect(
key1 = Unit,
) {
focusRequester.requestFocus()
keyboardController?.hide()
}
Sample code
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun FocusedTextFieldWithoutKeyboard() {
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember {
FocusRequester()
}
LaunchedEffect(
key1 = Unit,
) {
focusRequester.requestFocus()
keyboardController?.hide()
}
val initialText = "Sample Text"
var text by remember {
mutableStateOf(
value = TextFieldValue(
text = "Sample Text",
selection = TextRange(
start = initialText.length,
end = initialText.length,
),
),
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box {
BasicTextField(
value = text,
onValueChange = { value: TextFieldValue ->
text = value
},
modifier = Modifier
.focusRequester(focusRequester),
singleLine = true,
)
Box(
modifier = Modifier
.matchParentSize()
.alpha(0f)
.clickable(
onClick = {},
),
)
}
Row {
Button(
onClick = {
text = text.copy(
text = "${text.text}1",
selection = TextRange(
start = text.text.length + 1,
end = text.text.length + 1,
),
)
},
modifier = Modifier.padding(horizontal = 8.dp),
) {
Text(text = "1")
}
Button(
onClick = {
text = text.copy(
text = "${text.text}2",
selection = TextRange(
start = text.text.length + 1,
end = text.text.length + 1,
),
)
},
modifier = Modifier.padding(horizontal = 8.dp),
) {
Text(text = "2")
}
Button(
onClick = {
text = text.copy(
text = "${text.text}3",
selection = TextRange(
start = text.text.length + 1,
end = text.text.length + 1,
),
)
},
modifier = Modifier.padding(horizontal = 8.dp),
) {
Text(text = "3")
}
}
}
}
Related
I have 2 Radio Buttons to change the text color of a text (red text, hardcoded).
But i cant get the Text(color = Color.colorsTextRadio) to work.
I know it says it is a string but who do i get the Red or Green to convert to color.
If i have done something that could be better in thecode please tell because I'm a beginner.
#Composable
fun MainScreen() {
/**
* Text
*/
var text by remember {
mutableStateOf("test")
}
// Event handler
val onTextChange = { value: String ->
text = value
}
/**
* Colors
*/
val colors = listOf("Red", "Green")
var colorsTextRadio by remember {
mutableStateOf(colors[0])
}
// Event Handler
val onTextColorChange = { value: String ->
colorsTextRadio = value
}
Log.d("TAG", "MainScreen: colorsTextRadio $colorsTextRadio")
Column(modifier = Modifier.padding(6.dp)) {
TextField(value = text, onValueChange = onTextChange)
Text(text = text.replace("\n", " "), maxLines = 1, color = Color.Red)
RadioButtonGroup(colors = colors, colorsTextRadio = colorsTextRadio, onClick = onTextColorChange)
}
}
#Composable
fun RadioButtonGroup(
colors: List<String>,
colorsTextRadio: String,
onClick: (String) -> Unit
) {
Column(modifier = Modifier.selectableGroup()) {
colors.forEach { label ->
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.selectable(
selected = (colorsTextRadio == label),
onClick = { onClick.invoke(label) },
role = Role.RadioButton
)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
modifier = Modifier.padding(end = 16.dp),
selected = (colorsTextRadio == label),
onClick = null // null recommended for accessibility with screen readers
)
Text(text = label)
}
}
}
}
You can define a data class:
data class ColorLabel(
val label: String,
val color: Color
)
val colors = listOf(
ColorLabel("Red",Red),
ColorLabel("Green", Green))
var colorsTextRadio by remember { mutableStateOf(colors[0].label) }
var colorsText by remember { mutableStateOf(colors[0].color) }
Then apply them to your Composables:
Text(text = text.replace("\n", " "),
maxLines = 1,
color = colorsText)
Column(modifier = Modifier.selectableGroup()) {
colors.forEach { colorlabel->
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.selectable(
selected = (colorsTextRadio == colorlabel.label),
onClick = {
colorsTextRadio = colorlabel.label
colorsText = colorlabel.color
},
role = Role.RadioButton
)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
modifier = Modifier.padding(end = 16.dp),
selected = (colorsTextRadio == colorlabel.label),
onClick = null // null recommended for accessibility with screen readers
)
Text(text = colorlabel.label)
}
}
}
I am try to learning text field in android jetpack compose, so I have two text field in a screen, and when I typing somethings in first text field, I want to close keyboard when I click the space on screen. I was using
.pointerInput(Unit) {
detectTapGestures(onTap = {
focusManager.clearFocus()
})
}
this line of code for it, it work, but it is not work for multi textfield like 10 textfield, when I click the 8.textfield for example, bottom screen looks black. I do not have any idea why it is black? Any idea?
#Composable
fun KeyboardSample(){
val focusManager = LocalFocusManager.current
Column(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures(onTap = {
focusManager.clearFocus()
})
}
.padding(start = 16.dp, end = 16.dp),
) {
var name by rememberSaveable { mutableStateOf("") }
val updateName = { _name : String ->
name = _name
}
var amount by rememberSaveable { mutableStateOf("") }
val updateAmount = { _amount : String ->
amount = _amount
}
TextFiledsToExperiment(
name = name,
updateName = updateName,
amount = amount,
updateAmount = updateAmount
)
}
}
#Composable
fun TextFiledsToExperiment(
name : String,
updateName : (String) -> Unit,
amount : String,
updateAmount : (String) -> Unit
){
val focusManager = LocalFocusManager.current
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
OutlinedTextField(
value = name,
onValueChange = updateName ,
label = { Text("Name") },
placeholder = { Text(text = "Name") },
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = true,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(FocusDirection.Down)
}),
modifier = Modifier
.fillMaxWidth()
.padding(top = 6.dp, start = 0.dp, end = 0.dp, bottom = 6.dp),
)
OutlinedTextField(
value = amount,
onValueChange = updateAmount ,
label = { Text("Amount") },
placeholder = { Text(text = "Amount") },
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = true,
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
focusManager.clearFocus()
}),
modifier = Modifier
.fillMaxWidth()
.padding(top = 6.dp, start = 0.dp, end = 0.dp, bottom = 6.dp),
)
}
}
You can simply create clickable modifier in your column and run hide function in there.
val keyboardController = LocalSoftwareKeyboardController.current
Column(Modifier.clickable{keyboardController?.hide()}){
//
}
I using Jetpack Compose to make the application UI, but it has some issues: when the next button is clicked on the keyboard it moves the focus to the next TextField(Amount), which is fine, but the problem is the TextField keeps behind the keyboard, that means the screen animation is not triggering:
How to move the screen to the new next input?
#Composable
fun KeyboardSample() {
Scaffold(
modifier = Modifier
.fillMaxSize()
.padding(start = 16.dp, end = 16.dp),
) {
val name = rememberSaveable { mutableStateOf("") }
val updateName = { _name: String ->
name.value = _name
}
val amount = rememberSaveable { mutableStateOf("") }
val updateAmount = { _amount: String ->
amount.value = _amount
}
TextFieldsToExperiment(
name = name.value,
updateName = updateName,
amount = amount.value,
updateAmount = updateAmount
)
}
}
#Composable
fun TextFieldsToExperiment(
name: String,
updateName: (String) -> Unit,
amount: String,
updateAmount: (String) -> Unit
) {
val focusManager = LocalFocusManager.current
val focusRequester = FocusRequester()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Spacer(modifier = Modifier.height(390.dp))
OutlinedTextField(
value = name,
onValueChange = updateName,
label = { Text("Name") },
placeholder = { Text(text = "Name") },
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = true,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(FocusDirection.Down)
}),
modifier = Modifier
.fillMaxWidth()
.padding(top = 6.dp, start = 0.dp, end = 0.dp, bottom = 6.dp),
)
Text(text = "Hello")
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = amount,
onValueChange = updateAmount,
label = { Text("Amount") },
placeholder = { Text(text = "Amount") },
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
autoCorrect = true,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
focusManager.clearFocus()
}),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.padding(top = 6.dp, start = 0.dp, end = 0.dp, bottom = 6.dp),
)
}
}
Also, I add some pictures to illustrate better the issue:
UI without keyboard:
Name Focused
Amount Focused
Put your KeyboardSample() content in a LazyColumn like this
#Composable
fun KeyboardSample() {
Scaffold(
modifier = Modifier
.fillMaxSize()
.padding(
start = 16.dp,
end = 16.dp
),
) {
val name = rememberSaveable { mutableStateOf("") }
val updateName = { _name: String ->
name.value = _name
}
val amount = rememberSaveable { mutableStateOf("") }
val updateAmount = { _amount: String ->
amount.value = _amount
}
LazyColumn{
item {
TextFieldsToExperiment(
name = name.value,
updateName = updateName,
amount = amount.value,
updateAmount = updateAmount
)
}
}
}
}
And most importantly add this to your activity in the manifest
android:windowSoftInputMode="adjustResize"
Just add thiswindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)after onCreate
did you try android:windowSoftInputMode="adjustPan" it moves the window up so that your textfield became visible
I was making a login for my app in the new android jetpack's compose.
I want to make a OTP layout like in the given photo.
check full example here
const val PIN_VIEW_TYPE_UNDERLINE = 0
const val PIN_VIEW_TYPE_BORDER = 1
#Composable
fun PinView(
pinText: String,
onPinTextChange: (String) -> Unit,
digitColor: Color = MaterialTheme.colors.onBackground,
digitSize: TextUnit = 16.sp,
containerSize: Dp = digitSize.value.dp * 2,
digitCount: Int = 4,
type: Int = PIN_VIEW_TYPE_UNDERLINE,
) {
BasicTextField(value = pinText,
onValueChange = onPinTextChange,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
decorationBox = {
Row(horizontalArrangement = Arrangement.SpaceBetween) {
repeat(digitCount) { index ->
DigitView(index, pinText, digitColor, digitSize, containerSize, type = type)
Spacer(modifier = Modifier.width(5.dp))
}
}
})
}
#Composable
private fun DigitView(
index: Int,
pinText: String,
digitColor: Color,
digitSize: TextUnit,
containerSize: Dp,
type: Int = PIN_VIEW_TYPE_UNDERLINE,
) {
val modifier = if (type == PIN_VIEW_TYPE_BORDER) {
Modifier
.width(containerSize)
.border(
width = 1.dp,
color = digitColor,
shape = MaterialTheme.shapes.medium
)
.padding(bottom = 3.dp)
} else Modifier.width(containerSize)
Column(horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Text(
text = if (index >= pinText.length) "" else pinText[index].toString(),
color = digitColor,
modifier = modifier,
style = MaterialTheme.typography.body1,
fontSize = digitSize,
textAlign = TextAlign.Center)
if (type == PIN_VIEW_TYPE_UNDERLINE) {
Spacer(modifier = Modifier.height(2.dp))
Box(
modifier = Modifier
.background(digitColor)
.height(1.dp)
.width(containerSize)
)
}
}
}
You can use a very simple layout for each char in the otp.
Something like
#Composable
fun OtpChar(){
var text by remember { mutableStateOf("1") }
val maxChar = 1
Column(Modifier.background(DarkGray),
horizontalAlignment = Alignment.CenterHorizontally){
TextField(
value =text,
onValueChange = {if (it.length <= maxChar) text = it},
modifier = Modifier.width(50.dp),
singleLine = true,
textStyle = LocalTextStyle.current.copy(
fontSize = 20.sp,
textAlign= TextAlign.Center),
colors= TextFieldDefaults.textFieldColors(
textColor = White,
backgroundColor = Transparent,
unfocusedIndicatorColor = Transparent,
focusedIndicatorColor = Transparent)
)
Divider(Modifier
.width(28.dp)
.padding(bottom = 2.dp)
.offset(y=-10.dp),
color = White,
thickness = 1.dp)
}
}
You can add some features like:
manage the focus in Next direction with the TAB key
manage the focus in Previous direction with the BACK SPACE key
how to move to the next textfield when a digit is entered
Something like:
fun OtpChar(
modifier: Modifier = Modifier
){
val pattern = remember { Regex("^[^\\t]*\$") } //to not accept the tab key as value
var (text,setText) = remember { mutableStateOf("") }
val maxChar = 1
val focusManager = LocalFocusManager.current
LaunchedEffect(
key1 = text,
) {
if (text.isNotEmpty()) {
focusManager.moveFocus(
focusDirection = FocusDirection.Next,
)
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally
){
TextField(
value =text,
onValueChange = {
if (it.length <= maxChar &&
((it.isEmpty() || it.matches(pattern))))
setText(it)
},
modifier = modifier
.width(50.dp)
.onKeyEvent {
if (it.key == Key.Tab) {
focusManager.moveFocus(FocusDirection.Next)
true
}
if (text.isEmpty() && it.key == Key.Backspace) {
focusManager.moveFocus(FocusDirection.Previous)
}
false
},
textStyle = LocalTextStyle.current.copy(
fontSize = 20.sp,
textAlign= TextAlign.Center),
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next
),
colors= TextFieldDefaults.textFieldColors(
backgroundColor = Transparent,
unfocusedIndicatorColor = Transparent,
focusedIndicatorColor = Transparent),
)
Divider(
Modifier
.width(28.dp)
.padding(bottom = 2.dp)
.offset(y = -10.dp),
color = Teal200,
thickness = 1.dp)
}
}
Then just use something like a Row to display 4 OtpChars
val (item1, item2, item3, item4) = FocusRequester.createRefs()
Row(horizontalArrangement = Arrangement.SpaceBetween){
OtpChar(
modifier = Modifier
.focusRequester(item1)
.focusProperties {
next = item2
previous = item1
}
)
OtpChar(
modifier = Modifier
.focusRequester(item2)
.focusProperties {
next = item3
previous = item1
}
)
OtpChar(
modifier = Modifier
.focusRequester(item3)
.focusProperties {
next = item4
previous = item2
}
)
OtpChar(
modifier = Modifier
.focusRequester(item4)
.focusProperties {
previous = item3
next = item4
}
)
//....
}
If you faced keyboard issues try the code below:
#Composable
fun OtpCell(
modifier: Modifier,
value: String,
isCursorVisible: Boolean = false
) {
val scope = rememberCoroutineScope()
val (cursorSymbol, setCursorSymbol) = remember { mutableStateOf("") }
LaunchedEffect(key1 = cursorSymbol, isCursorVisible) {
if (isCursorVisible) {
scope.launch {
delay(350)
setCursorSymbol(if (cursorSymbol.isEmpty()) "|" else "")
}
}
}
Box(
modifier = modifier
) {
Text(
text = if (isCursorVisible) cursorSymbol else value,
style = MaterialTheme.typography.body1,
modifier = Modifier.align(Alignment.Center)
)
}
}
#ExperimentalComposeUiApi
#Composable
fun PinInput(
modifier: Modifier = Modifier,
length: Int = 5,
value: String = "",
onValueChanged: (String) -> Unit
) {
val focusRequester = remember { FocusRequester() }
val keyboard = LocalSoftwareKeyboardController.current
TextField(
value = value,
onValueChange = {
if (it.length <= length) {
if (it.all { c -> c in '0'..'9' }) {
onValueChanged(it)
}
if (it.length >= length) {
keyboard?.hide()
}
}
},
// Hide the text field
modifier = Modifier
.size(0.dp)
.focusRequester(focusRequester),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
repeat(length) {
OtpCell(
modifier = modifier
.size(width = 45.dp, height = 60.dp)
.clip(MaterialTheme.shapes.large)
.background(MaterialTheme.colors.surface)
.clickable {
focusRequester.requestFocus()
keyboard?.show()
},
value = value.getOrNull(it)?.toString() ?: "",
isCursorVisible = value.length == it
)
if (it != length - 1) Spacer(modifier = Modifier.size(8.dp))
}
}
}
Result:
First make the common TextField for OTP Screen
#Composable
fun CommonOtpTextField(otp: MutableState<String>) {
val max = 1
OutlinedTextField(
value = otp.value,
singleLine = true,
onValueChange = { if (it.length <= max) otp.value = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
shape = RoundedCornerShape(20.dp),
modifier = Modifier
.width(60.dp)
.height(60.dp),
maxLines = 1,
textStyle = LocalTextStyle.current.copy(
textAlign = TextAlign.Center
)
)
}
And Now use above CommonTextField to make four otp Field
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 15.dp, start = 15.dp, end = 15.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
CommonOtpTextField(otp = otpOne)
CommonOtpTextField(otp = otpTwo)
CommonOtpTextField(otp = otpThree)
CommonOtpTextField(otp = otpFour)
}
Simple solution that uses one TextField and different code length
#Composable
fun RegistrationCodeInput(codeLength: Int) {
val code = remember { mutableStateOf("") }
val focusRequester = FocusRequester()
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
BasicTextField(
value = code.value,
onValueChange = { if (it.length <= codeLength) code.value = it },
Modifier.focusRequester(focusRequester = focusRequester),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
decorationBox = {
CodeInputDecoration(code.value, codeLength)
})
}
}
#Composable
private fun CodeInputDecoration(code: String, length: Int) {
Box(
modifier = Modifier
.padding(16.dp)
.border(
border = BorderStroke(2.dp, color = borderColor),
shape = Shapes.small
)
) {
Row(
) {
for (i in 0 until length) {
val text = if (i < code.length) code[i].toString() else ""
CodeEntry(text)
}
}
}
}
#Composable
private fun CodeEntry(text: String) {
Box(
modifier = Modifier
.width(42.dp)
.height(42.dp),
contentAlignment = Alignment.Center
) {
Text(text = text)
}
}
#Preview
#Composable
fun PreviewInput() {
RegistrationCodeInput(4)
}
I have five TextFields. After something entered in first field focus moves for next textfield.
If i deleted something in text field - focus moves for pervious textfield.
All work with focus goes through onValueChanged section
But if value in textfield blank("") - if i press backspace in keyboard nothing happened with onValueChanged, because value in field not changed. And I needed somehow set focus on previous textfield
So how i can use listener for back press in soft-keyboard for text field in compose?
I tried use KeyboardActions,
keyboardActions = KeyboardActions(
onPrevious = {
//some work with foucs
},
),
but it not working
And second question:
if textfield got clicked(or get focus) how to set cursor in field on end text?
I needed even if user click middle of string cursor sets on end.
With solution #nglauber
Textfield for enter sms(for now):
#Composable
fun SMSTextFields(
modifier: Modifier,
smsCodeLength: Int = 5,
whenFull: (smsCode: String) -> Unit
) {
val enteredNumbers = remember {
mutableStateListOf(
*((0 until smsCodeLength).map { "" }.toTypedArray())
)
}
val focusRequesters: List<FocusRequester> = remember {
(0 until smsCodeLength).map { FocusRequester() }
}
Row(modifier = modifier.padding(start = 60.dp, end = 60.dp)) {
(0 until smsCodeLength).forEach { index ->
TextField(
modifier = Modifier
.weight(1f)
.size(120.dp, 80.dp)
.onKeyEvent { event ->
val cellValue = enteredNumbers[index]
if (event.type == KeyEventType.KeyUp) {
if (event.key == Key.Backspace && cellValue == "") {
focusRequesters
.getOrNull(index - 1)
?.requestFocus()
enteredNumbers[index - 1] = ""
} else if (cellValue != "") {
focusRequesters
.getOrNull(index + 1)
?.requestFocus()
}
}
false
}
.padding(vertical = 2.dp)
.focusOrder(focusRequesters[index])
.focusRequester(focusRequesters[index]),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = whiteBackground,
unfocusedIndicatorColor = greyColor,
focusedIndicatorColor = signUpColorButton,
cursorColor = greyColor,
textColor = greyColor
),
textStyle = smsCodeEnterStyle,
singleLine = true,
value = enteredNumbers[index],
onValueChange = { value: String ->
if (value.isDigitsOnly()) {
if (value.length > 1) {
enteredNumbers[index] = value.last().toString()
return#TextField
}
if (focusRequesters[index].freeFocus()) {
enteredNumbers[index] = value
if (enteredNumbers[index].isBlank() && index > 0 && index <5) {
focusRequesters[index - 1].requestFocus()
} else if (index < smsCodeLength - 1) {
focusRequesters[index + 1].requestFocus()
}
else if (enteredNumbers.size == 5){
whenFull(enteredNumbers.joinToString(separator = ""))
}
}
}
},
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
)
Spacer(modifier = Modifier.width(4.dp))
}
}
}
#ExperimentalComposeUiApi
#Composable
fun EnterOtpView() {
val scrollState = rememberScrollState()
val focusManager = LocalFocusManager.current
val digits = remember {
mutableStateListOf(
*((0 until 4).map { "" }.toTypedArray())
)
}
val focusRequesters: List<FocusRequester> = remember {
(0 until 4).map { FocusRequester() }
}
Column(
Modifier.padding(horizontal = 70.dp),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "123 456 8550", style = MaterialTheme.typography.h3, color = Purple,
modifier = Modifier.padding(top = 20.dp, bottom = 30.dp)
)
Row(
modifier = Modifier
.fillMaxWidth(1f)
.wrapContentHeight(),
Arrangement.SpaceBetween,
Alignment.CenterVertically
) {
(0 until 4).forEach { index ->
TextField(
modifier = Modifier
.weight(0.2f)
.padding(end = 4.dp)
.onKeyEvent {
if (it.nativeKeyEvent.keyCode == 67) {
if (digits[index].isEmpty()) {
focusManager.moveFocus(FocusDirection.Left)
}
digits[index] = ""
}
true
}
.padding(vertical = 2.dp)
.focusOrder(focusRequesters[index])
.focusRequester(focusRequesters[index]),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent
),
singleLine = true,
textStyle = MaterialTheme.typography.body1.copy(textAlign = TextAlign.Center),
value = digits[index],
onValueChange = {
if (digits[index].isEmpty() && it.isDigitsOnly()) {
digits[index] = it
focusManager.moveFocus(FocusDirection.Right)
}
},
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next,
keyboardType = KeyboardType.Number
),
keyboardActions = KeyboardActions(
onNext = {
focusManager.moveFocus(FocusDirection.Right)
}
)
)
}
}
}}
Worked for me using nativeKeyEvent and focusManager.