OtpInputField

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.

Parameters

state

Holds the entered text. Use rememberTextFieldState() so it survives configuration changes and process death.

otpLength

Number of cells. Must be positive.

enabled

When false, the field can't be focused or edited and cells are drawn faded.

isError

Shows error colors. Switching it to true also shakes the cells, see animationSpec.

placeholder

Text drawn in empty cells that aren't focused.

maskConfig

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.

inputTransformation

Extra filtering applied after the built-in digits and length check.

onComplete

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.

separator

Drawn between cells instead of a OtpCellDimensions.spacing gap. Receives the index of the cell on its left.

cell

Draws one cell.


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.