I'd want to add phone number formatting as the user types in the field. So far, it's functioning, but the cursor is in the wrong location when the field is empty. When we have the initial value, it moves the cursor to the right location.
Note: I would like to provide support to add number in between characters.
Example: 1234567890 -> +1 (123) 456-7890
When empty -> +1
Type 1 -> +1 (1
Type 2 -> +1 (12
Type 3 -> +1 (123)
I would like to display just +1 when field is empty and if user type first character I would like to add (.
Here is my effort.
/**
* The visual filter for phone number.
*
* This filter converts up to 10 digits to phone number form.
* For example, "1234567890" will be shown as "+1 (123) 456-7890".
*/
private val phoneNumberFilter = VisualTransformation { text ->
// +1 (XXX) XXX-XXXX
val trimmed = if (text.text.length >= 10) text.text.substring(0..9) else text.text
var out = ""
for (i in trimmed.indices) {
out += trimmed[i]
when (i) {
// 0 -> out+= "$out"
2 -> out += ") "
5 -> out += "-"
}
}
out = "+1 ($out"
val mapping = object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
Log.d("TAG", "Out: $out")
val transformedOffsets = out
.mapIndexedNotNull { index, c ->
index
.takeIf { c.isDigit() }
// convert index to an offset
?.plus(1)
}
// We want to support an offset of 0 and shift everything to the right,
// so we prepend that index by default
.let { offsetList ->
listOf(0) + offsetList
}
// val transformedOffsets = offset + countSpecialChars(out) + 1
Log.d("TAG", "originalToTransformed: $transformedOffsets")
return transformedOffsets[offset+1]
}
override fun transformedToOriginal(offset: Int): Int {
val originalOffset = out
// This creates a list of all separator offsets
.mapIndexedNotNull { index, c ->
index.takeIf { !c.isDigit() }
}
// We want to count how many separators precede the transformed offset
.count { separatorIndex ->
separatorIndex < offset
}
// We find the original offset by subtracting the number of separators
.let { separatorCount ->
offset - separatorCount
}
// val originalOffset = offset - countSpecialChars(out) - 1
Log.d("TAG", "transformedToOriginal: $originalOffset")
return originalOffset-1
}
}
UI
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = phone,
onValueChange = { value ->
if (value.length <= 10) {
phone = value.takeWhile { it.isDigit() }
}
Log.d("TAG", "PhoneNumber: $phone")
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
visualTransformation = remember { phoneNumberFilter }
)