fun OtpInputField(state: TextFieldState, otpLength: Int, modifier: Modifier = Modifier, enabled: Boolean = true, isError: Boolean = false, placeholder: String = "", colors: OtpCellColors = OtpInputDefaults.colors(), dimensions: OtpCellDimensions = OtpInputDefaults.dimensions(), animationSpec: OtpAnimationSpec = OtpInputDefaults.animationSpec(), shape: Shape = OtpInputDefaults.shape(dimensions.cornerRadius), maskConfig: OtpMaskConfig = OtpInputDefaults.maskConfig(), cursorConfig: OtpCursorConfig = OtpInputDefaults.cursorConfig(color = colors.cursorColor), textStyles: OtpTextStyles = OtpInputDefaults.textStyles(dimensions.textSize), contentPadding: OtpCellPadding = OtpInputDefaults.contentPadding(), keyboardOptions: KeyboardOptions = OtpInputDefaults.keyboardOptions, inputTransformation: InputTransformation? = null, onComplete: (code: String) -> Unit? = null, separator: @Composable (afterIndex: Int) -> Unit? = null, cell: @Composable (OtpCellState) -> Unit = { cellState ->
DefaultOtpCell(
state = cellState,
placeholder = placeholder,
colors = colors,
dimensions = dimensions,
animationSpec = animationSpec,
shape = shape,
maskConfig = maskConfig,
cursorConfig = cursorConfig,
textStyles = textStyles,
contentPadding = contentPadding,
)
})(source) An input for one-time codes that draws each digit in its own cell.
Only ASCII digits are accepted and the text never grows past otpLength, whether it's typed, pasted, autofilled or set through fillOtp. The field is marked as an SMS one-time code for autofill, so keyboards like Gboard can offer the code from a new message.
val otpState = rememberTextFieldState()
OtpInputField(
state = otpState,
otpLength = 6,
onComplete = { code -> viewModel.verify(code) },
)
Pass a cell to change how cells look, for example with UnderlineOtpCell or OutlinedOtpCell. The styling parameters here only affect the default cell.
Holds the entered text. Use rememberTextFieldState() so it survives configuration changes and process death.
Number of cells. Must be positive.
When false, the field can't be focused or edited and cells are drawn faded.
Shows error colors. Switching it to true also shakes the cells, see animationSpec.
Text drawn in empty cells that aren't focused.
Hides digits after a short delay. When masking is on, accessibility services treat the field as a password and don't read the digits aloud.
Extra filtering applied after the built-in digits and length check.
Called each time the field goes from incomplete to holding otpLength digits. It isn't called for a value that was already complete when the field appeared, so restoring state after rotation doesn't submit the code twice.
fun OtpInputField(value: String, onValueChange: (String) -> Unit, otpLength: Int, modifier: Modifier = Modifier, enabled: Boolean = true, isError: Boolean = false, placeholder: String = "", colors: OtpCellColors = OtpInputDefaults.colors(), dimensions: OtpCellDimensions = OtpInputDefaults.dimensions(), animationSpec: OtpAnimationSpec = OtpInputDefaults.animationSpec(), shape: Shape = OtpInputDefaults.shape(dimensions.cornerRadius), maskConfig: OtpMaskConfig = OtpInputDefaults.maskConfig(), cursorConfig: OtpCursorConfig = OtpInputDefaults.cursorConfig(color = colors.cursorColor), textStyles: OtpTextStyles = OtpInputDefaults.textStyles(dimensions.textSize), contentPadding: OtpCellPadding = OtpInputDefaults.contentPadding(), keyboardOptions: KeyboardOptions = OtpInputDefaults.keyboardOptions, onComplete: (code: String) -> Unit? = null, separator: @Composable (afterIndex: Int) -> Unit? = null, cell: @Composable (OtpCellState) -> Unit = { cellState ->
DefaultOtpCell(
state = cellState,
placeholder = placeholder,
colors = colors,
dimensions = dimensions,
animationSpec = animationSpec,
shape = shape,
maskConfig = maskConfig,
cursorConfig = cursorConfig,
textStyles = textStyles,
contentPadding = contentPadding,
)
})(source) OtpInputField for state you keep as a String, for example in a ViewModel's UI state.
Prefer the TextFieldState overload when you can. It handles IME edits more reliably and saves its own state. Every value passed to onValueChange is already filtered to digits and trimmed to otpLength.
OtpInputField(
value = uiState.code,
onValueChange = viewModel::onCodeChange,
otpLength = 6,
)
See the other overload for the remaining parameters.