OTP/Pin Input Made Easy in Jetpack Compose

Using OhTeePee library.

ilyas ipek
Kt. Academy

--

Designed by Tarik Yasar

If you try to implement an OTP/Pin Input, you would think it’s easy till you run into 500 behavior bugs.

Encountering identical obstacles, Tarik Yasar & I crafted OhTeePee, a simple, customizable, and easy-to-use library. Soo let me show you how you can use it in your app 😁

1- Add Dependency

Add the Jitpack repository to your root build.gradle file. If you’re using the settings.gradle file, include it there.

repositories {
...
maven { url 'https://jitpack.io' }
}

Then add OhTeePee dependency to your module build.gradle file.

def ohTeePeeVersion = "1.0.3"
implementation "com.github.composeuisuite:ohteepee:$ohTeePeeVersion"

Then click Sync Now

2- Create a basic OTP composable

First, let’s build a basic composable then build on it…

@Composable
fun OtpInput() {
// a mutable state to handle OTP value changes…
var otpValue: String by remember { mutableStateOf("") }

// this config will be used for each cell
val defaultCellConfig = OhTeePeeCellConfiguration.withDefaults(
borderColor = Color.LightGray,
borderWidth = 1.dp,
shape = RoundedCornerShape(16.dp),
textStyle = TextStyle(
color = Color.Black
)
)

OhTeePeeInput(
value = otpValue,
onValueChange = { newValue, isValid ->
otpValue = newValue
},
configurations = OhTeePeeConfigurations.withDefaults(
cellsCount = 6,
emptyCellConfig = defaultCellConfig,
cellModifier = Modifier
.padding(horizontal = 4.dp)
.size(48.dp),
),
)
}

let’s make it more beautiful by adding a placeHolder & a different cell config…

@Composable
fun OtpInput() {
...

OhTeePeeInput(
...
configurations = OhTeePeeConfigurations.withDefaults(
...,
emptyCellConfig = defaultCellConfig,
filledCellConfig = defaultCellConfig,
activeCellConfig = defaultCellConfig.copy(
borderColor = Color.Blue,
borderWidth = 2.dp
),
errorCellConfig = defaultCellConfig.copy(
borderColor = Color.Red,
borderWidth = 2.dp
),
placeHolder = "-", // a place holder (great comment isn't it)
),
)
}

Note: When you use OhTeePeeConfigurations.withDefaults() function, you just need to pass emptyCellConfig and the rest would be taken care of using your MaterialTheme.color ‘s config (errorColor and primaryColor).

Also, you can pass obscureText parameter to replace the entered value with an x character, this can be used in password/pins…

@Composable
fun OtpInput() {
OhTeePeeInput(
...
configurations = OhTeePeeConfigurations.withDefaults(
obscureText = "*"
),
)
}

3- Make it functional 🤩

We need basically to do the following…

1- Validate the OTP/Pin code when the user fills all of the cells.
2- Disable the user from editing when validating the code.
3- Handle error stats.

enum class OtpState{Loading, Error, None}

@Composable
fun OtpInput() {
...
// Normally this state should be in your vm or UiState
var state: State by remember { mutableStateOf(State.None) }

OhTeePeeInput(
value = otpValue,
onValueChange = { newValue, isValid ->
otpValue = newValue
// isValid is equal to true when user fills all cells
if (isValid) {
state = OtpState.Loading
// Send a request to validate the value here...
vm.validateOtp(otpValue)
} else {
// Reset the state when user enters (to remove the error state)
state = OtpState.None
}
},
// Disable the user from editing the input when loading
enabled = state != OtpState.Loading,
// When isValueInvalid is true we will use errorCellConfig state
isValueInvalid = state == OtpState.Error,
...
)
}

Let’s say the right code is “123456”

That’s it 🎉 check out our repo for more examples or to contribute, and of course give it a ⭐ if you find it useful 😉

Kt. Academy blog

--

--

Android developer @teknasyon, writes about Android development and productivity.