OTP/Pin Input Made Easy in Jetpack Compose
Using OhTeePee library.
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 passemptyCellConfig
and the rest would be taken care of using yourMaterialTheme.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 😉