Regex for TextField is not working as expected in kotlin?

50 Views Asked by At

I require certain constraints in my project, particularly within the regex, to restrict user input and format it appropriately. These rules are intended for implementation within the TextField component in my Jetpack Compose project.

Rules:

  • Zero and Dot cannot be at the start.
  • There can only be one dot.
  • After the dot, there can only be two digits, and before the dot, there can only be four digits.
  • If the user does not enter a dot, they can only input four digits. If they do enter a dot, they can add two digits after it.

Examples: (These examples are provided for reference purposes only.)

  • 8956.32 -> Valid
  • 45 -> Valid
  • 562.2 -> Valid
  • 0655 -> Invalid
  • 0356.00 -> Invalid
  • .003 -> Invalid

I tried belwo code but this is not working as expected.

var text by remember { mutableStateOf("") }
val regex = remember { Regex("^(?!0|\\.)(?!.*\\.\\.)\\d{1,4}(\\.\\d{1,2})?$") }

TextField(
                label = R.string.label_days,
                value = text,
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Number
                ),
                onValueChange = { days ->
                    if (days.isEmpty() || regex.matches(days)){
                     text = days
                    }
                })
2

There are 2 best solutions below

1
Ravi Sharma On BEST ANSWER

Below code will work for your requirement

    var text by remember { mutableStateOf("") }
    val regex = remember { Regex("^(?!0)(?!.*\\.\\..*|\\.$)\\d{0,4}(\\.\\d{0,2})?\$") }
    TextField(
        label = {
            Text(text = "Days")
        },
        value = text,
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number
        ),
        onValueChange = { days ->
            if (days.isEmpty() || days.matches(regex)) {
                text = days
            }
        }
    )

2
Jan Itor On

You have 2 options here:

  • Allow only valid input. In this case you also have to allow and handle the trailing dot. Also it is better to use TextFieldValue variant of the TextField to prevent cursor movement on invalid input.
  • Allow any input but indicate when the input is invaid and only accept if it is correct. In this case you don't have to allow the trailing dot.

With the regex pattern by @Leviathan it would look like this:

Option 1:

val regex = remember { Regex("^[1-9]\\d{0,3}(\\.\\d{0,2})?$") }
var text by rememberSaveable(stateSaver = TextFieldValue.Saver) {
    mutableStateOf(TextFieldValue(""))
}
Row {
    TextField(
//        label = R.string.label_days,
        label = { Text("Days") },
        value = text,
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number
        ),
        onValueChange = { days: TextFieldValue ->
            if (days.text.isEmpty() || regex.matches(days.text)) {
                text = days
            }
        }
    )
    Button(
        onClick = {},
    ) {
        Text("Always OK")
    }
}

Option 2:

val regex = remember { Regex("^[1-9]\\d{0,3}(\\.\\d{1,2})?$") }
var text by remember { mutableStateOf("") }
var isValid by remember { mutableStateOf(true) }

Row {
    TextField(
//        label = R.string.label_days,
        label = { Text("Days") },
        value = text,
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number
        ),
        supportingText = {
            if (!isValid) {
                Text("Invalid input")
            }
        },
        isError = !isValid,
        onValueChange = { days: String ->
            isValid = (days.isEmpty() || regex.matches(days))
            text = days
        }
    )
    Button(
        onClick = {},
        enabled = isValid,
    ) {
        Text("OK")
    }
}