Create A Custom Code Entry TextField In Android Compose

You are currently viewing Create A Custom Code Entry TextField In Android Compose

In this post I will show you two different versions of a code entry TextField I created in Android Compose.

The root element used to create these two objects are BasicTextField.

BasicTextField is like a normal TextField but it has more customizable features to it. These elements I will show you take advantage of the decorationBox feature.

Code

First method I will show you with the decoration of each character having a simple underline decoration.

@Composable
fun CodeTextField() {

    var text = remember{mutableStateOf("")}

    val fixedLength = 6

    var lastDigit = rememeber{mutableStateOf(0)}

    BasicTextField(
        value = text,
        onValueChange = {
            if (it.length <= fixedLength) {
                text = it
                lastDigit = text.length
            }
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        decorationBox = {
            Row {
                repeat(fixedLength) { index ->
                    Column(
                        modifier = Modifier
                            .padding(5.dp)
                            .drawBehind {
                                drawLine(
                                    color = Color.Black,
                                    start = Offset(0f, size.height),
                                    end = Offset(size.width, size.height),
                                    strokeWidth = 5f
                                )
                            }
                            .background(
                                if (lastDigit == index)
                                    Color.Gray.copy(alpha = 0.25f)
                                else Color.Gray.copy(alpha = 0.05f)
                            )
                    ) {
                        Text(
                            modifier = Modifier.padding(8.dp),
                            fontSize = 45.sp,
                            text = if (text.getOrNull(index) == null) " " else text[index].toString()
                        )

                    }
                }
            }
        }
    )
}

The second one is shown not too different but with a border around each character entered.

@Composable
fun BorderCodeTextField() {

    var text = remember{mutableStateOf("")}

    val fixedLength = 6

    var lastDigit = rememeber{mutableStateOf(0)}

    BasicTextField(
        value = text,
        onValueChange = {
            if (it.length <= fixedLength) {
                text = it
                lastDigit = text.length
            }
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        decorationBox = {
            Row {
                repeat(fixedLength) { index ->
                    Column(
                        modifier = Modifier
                            .padding(5.dp)
                            .border(1.dp, Color.Black, RoundedCornerShape(5.dp))
                            .background(
                                if (lastDigit == index)
                                    Color.Gray.copy(alpha = 0.25f)
                                else Color.Gray.copy(alpha = 0.05f)
                            )
                    ) {
                        Text(
                            modifier = Modifier.padding(8.dp),
                            fontSize = 45.sp,
                            text = if (text.getOrNull(index) == null) " " else text[index].toString()
                        )

                    }
                }
            }
        }
    )
}

Below is code I created to add both to the UI along with using the same string input and a callback to update.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SampleCodeEntryTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(modifier = Modifier.fillMaxSize().padding(innerPadding), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
                        var input by remember { mutableStateOf("") }
                        CodeTextField(input){
                            input = it
                        }
                        Spacer(modifier = Modifier.padding(25.dp))
                        BorderCodeTextField(input){
                            input = it
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun CodeTextField(input : String, callback : (String) -> Unit) {

    var text = input

    val fixedLength = 6

    var lastDigit = text.length

    BasicTextField(
        value = text,
        onValueChange = {
            if (it.length <= fixedLength) {
                text = it
                lastDigit = text.length
                callback(text)
            }
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        decorationBox = {
            Row {
                repeat(fixedLength) { index ->
                    Column(
                        modifier = Modifier
                            .padding(5.dp)
                            .drawBehind {
                                drawLine(
                                    color = Color.Black,
                                    start = Offset(0f, size.height),
                                    end = Offset(size.width, size.height),
                                    strokeWidth = 5f
                                )
                            }
                            .background(
                                if (lastDigit == index)
                                    Color.Gray.copy(alpha = 0.25f)
                                else Color.Gray.copy(alpha = 0.05f)
                            )
                    ) {
                        Text(
                            modifier = Modifier.padding(8.dp),
                            fontSize = 45.sp,
                            text = if (text.getOrNull(index) == null) " " else text[index].toString()
                        )

                    }
                }
            }
        }
    )
}

@Composable
fun BorderCodeTextField(input : String, callback : (String) -> Unit) {

    var text = input

    val fixedLength = 6

    var lastDigit = text.length

    BasicTextField(
        value = text,
        onValueChange = {
            if (it.length <= fixedLength) {
                text = it
                lastDigit = text.length
                callback(text)
            }
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        decorationBox = {
            Row {
                repeat(fixedLength) { index ->
                    Column(
                        modifier = Modifier
                            .padding(5.dp)
                            .border(1.dp, Color.Black, RoundedCornerShape(5.dp))
                            .background(
                                if (lastDigit == index)
                                    Color.Gray.copy(alpha = 0.25f)
                                else Color.Gray.copy(alpha = 0.05f)
                            )
                    ) {
                        Text(
                            modifier = Modifier.padding(8.dp),
                            fontSize = 45.sp,
                            text = if (text.getOrNull(index) == null) " " else text[index].toString()
                        )

                    }
                }
            }
        }
    )
}

Hope this was helpful.

Leave a Reply