Tinyrack

OTP Field

Collect a verification code through one accessible input rendered as individual slots.

Collect a verification code through one accessible input rendered as individual slots.

Contract

AxisContract
ValueThe default constructor is uncontrolled: seed it with defaultValue, or pass a TROtpFieldController to read and clear the code from outside. TROtpField.controlled takes value instead and expects you to store the next code from onValueChange.
InputOne hidden TextField sits under the slots, so typing, pasting a whole code, deleting, and platform autofill through AutofillHints.oneTimeCode all act on the full value. Entry always appends at the end; there is no per-slot caret to move between.
Accepted charactersallowedPattern defaults to RegExp("[0-9]") and is enforced by a FilteringTextInputFormatter, so anything else is dropped as it arrives. Rejection is silent: there is no invalid-input callback, so state the expected format in helperText.
Length and sizelength clamps the value and decides when onCompleted fires. uiSize scales the square slots along the shared control height scale, so md and lg line up with a neighboring TRTextField or TRButton of the same size.

Reach for TROtpField when the reader transcribes a short fixed-length code from another device. Use TRTextField for anything longer or free-form.

Install

Add the package, then import its public library.

flutter pub add tinyrack_ui
import 'package:tinyrack_ui/tinyrack_ui.dart';

Playground

Usage

import 'package:material_ui/material_ui.dart';
import 'package:tinyrack_ui/tinyrack_ui.dart';

TROtpField(
  label: 'Verification code',
  length: 6,
  helperText: 'Enter the code we sent to your device.',
  onCompleted: verifyCode,
)

Examples

Sizes permalink

Choose `uiSize` so the slots line up with the `TRTextField`, `TRButton`, or `TRNumberField` beside them. The digit style stays the same at every size; only the square slots and the default gap change.

Length and availability permalink

`length` sets the slot count and decides when `onCompleted` fires. `readOnly` keeps a code visible and focusable but rejects edits, while `enabled: false` mutes the whole field and blocks focus.

Required code and recovery permalink

`TROtpFieldFormField` joins the surrounding `Form`, so `validator` output lands in `errorText` and turns the slot borders red. `AutovalidateMode.onUserInteraction` reports a short code while the reader is still typing rather than only on submit.

Masked entry and reset permalink

`obscureText` replaces each digit with a bullet and stops the value from reaching `Semantics`, so use it only for a code that stays secret after entry. A `TROtpFieldController` clears the field from a Retry action, and `separatorBuilder` replaces the gap after the slot at `index` — return a plain `SizedBox` for the seams that should stay empty.

API

TROtpField properties

PropType / defaultPurpose
lengthint · 6Sets the slot count, clamps the value to that many characters, and gates onCompleted. Must be greater than zero.
defaultValueString · ''Seeds the uncontrolled default constructor. Ignored by TROtpField.controlled.
valueString · required on .controlledDrives the rendered code in the controlled constructor. Pair it with onValueChange and store the next code yourself.
controllerTROtpFieldController? · nullReads, replaces, or clears the code from outside the widget without switching to the controlled constructor.
onValueChangeValueChanged<String>? · nullFires on every accepted edit with the clamped value. Filtered-out characters never reach it.
onCompletedValueChanged<String>? · nullFires when the value reaches length, including when a paste fills the field in one step. It runs on every edit that leaves the field full, so make the callback safe to repeat.
allowedPatternPattern? · RegExp("[0-9]")Restricts accepted characters. Widen it for alphanumeric codes, for example RegExp("[A-Z0-9]").
obscureTextbool · falseReplaces each filled slot with a bullet and stops the value from being exposed through Semantics.
uiSizeTRUiSize · TRUiSize.mdScales the square slots and the default gap to the md or lg control height. A separatorBuilder replaces the gap entirely, so size it yourself there.
labelString? · nullRenders an uppercased caption above the slots and names the field for assistive technology unless semanticLabel overrides it.
semanticLabelString? · nullTakes precedence over label for assistive technology. Use it when the visible caption is too terse to stand alone.
helperTextString? · nullShows muted supporting text below the slots. errorText replaces it while an error is present.
errorTextString? · nullSwitches the slot borders to the danger color and replaces the supporting line. A non-null value marks the field invalid on its own; validation stays yours to run.
enabledbool · trueWhen false, mutes the slots, blocks tap-to-focus and editing, and dims the label.
readOnlybool · falseKeeps the field focusable and its value visible while rejecting edits. Use it for a code the reader should see but not change.
autofocusbool · falseFocuses the field on first build. Use it only when the code entry is the sole purpose of the screen.
separatorBuilderTROtpSeparatorBuilder? · nullReplaces the gap after slot index. Return a plain SizedBox for the seams that should stay empty; returning nothing is not an option.

TROtpFieldController

PropType / defaultPurpose
valueStringReads or replaces the current code. Assigning notifies listeners and re-renders the slots; assigning the same string is a no-op.
clear()voidEmpties the field. Pair it with a Retry action after a rejected code.

TROtpFieldFormField

PropType / defaultPurpose
initialValueString · ''Seeds the FormField state, which owns the value from then on.
validatorFormFieldValidator<String>? · nullReturns the message to show as errorText. Return null once the code is acceptable.
autovalidateModeAutovalidateMode? · nullChooses when the validator runs. onUserInteraction reports a short code as soon as the reader edits it.
onSavedFormFieldSetter<String>? · nullReceives the code when the surrounding Form is saved.
restorationIdString? · nullRestores the entered code after the platform recreates the route.