OtpCodeVerificationField class

Widget OtpCodeVerificationField is a verification code field, used for one-time-password (OTP) flows such as confirming an email address or a phone number.

What it does

A row of numberOfDigits rounded boxes is shown side by side. As the user types on the keyboard the characters fill in from left to right, with a blinking cursor in the next empty box and a highlighted border around it. Disallowed characters are silently rejected (see codeType, codeLetterSet, and codeLetterCasing) and the input is capped at numberOfDigits characters.

Tapping anywhere on the row of boxes opens the keyboard.

As soon as the last digit is typed, the code is submitted automatically (there is no separate "submit" button) by calling onSubmit. The widget then reacts to one of three outcomes:

  • Correct codeonSubmit returns true. The widget does nothing else; the owner is expected to navigate away to the next page.
  • Wrong codeonSubmit returns false. An "Enter the correct code" message appears below the digits, the boxes are cleared, and focus returns so the user can try again.
  • Verification failedonSubmit throws (e.g. no internet, server error). A "Could not verify the code" message appears below the digits along with a "Retry" button, but the typed digits are kept so the user can resubmit the same code with a single tap.

In both error cases the message disappears as soon as the user starts typing again.

Optional countdown

The widget can also show how long the code is still valid for, as a "Code valid for M:SS" line below the digits. When the timer hits zero the digit row is replaced by a "Your code expired" view with a "Go back to try again" button. Tapping it calls onExpiredGoBack — the widget itself does not navigate or change state, so the owner decides what happens next (typically: pop the page and request a fresh code).

Customization

Almost everything visual can be tweaked: digit box look, border colors (with a separate color for the active box), cursor color and size, text styles, the error/expired/countdown messages, and the containers that wrap each piece of text. If you need fully custom buttons (e.g. to match a design system) pass buttonBuilder and it will be used for both the "Retry" and "Go back to try again" buttons; otherwise a plain elevated button is used.


Parameters

Required

  • onSubmit — verifies the typed code. Returns true for correct, false for wrong, or throws on verification failure. The widget dismisses the keyboard before calling it.
  • countdownStartDateTime — the wall-clock time the code was issued at, used as the anchor for the countdown. Pass null to disable the countdown (the field then stays active indefinitely).
  • onExpiredGoBack — called when the user taps "Go back to try again" on the expired view. The widget itself does no navigation.

Allowed characters

All three of codeType, codeLetterCasing, and codeLetterSet are nullable. null means "user didn't say" — the widget infers a reasonable behavior from whichever params are set (see "Defaults & inference" below). Mutually-incompatible combinations are caught with asserts in the constructor.

  • codeType — what kind of characters are accepted: digits only, letters only, or both. null = unspecified.
  • codeLetterCasing — how letters are normalized (forced uppercase, forced lowercase, or kept as typed for mixed). null = no normalization is applied.
  • codeLetterSet — which alphabet of characters is accepted (a-z/A-Z, Unicode letters, hex, binary, Crockford base32, base62, "all", or a custom set). null = unspecified.
  • customSet — when codeLetterSet is OtpCodeLetterSet.custom, the string of characters that can be typed (digits 0-9 are added on top when codeType is OtpCodeType.numbersAndLetters). Required to be non-empty in that mode; ignored otherwise.

Defaults & inference

  • All three null → digits 0-9 only (= effective codeType=numbersOnly).
  • Only codeLetterCasing set → any character allowed (= effective codeLetterSet=all); letters are normalized per the casing.
  • Only codeLetterSet set → effective codeType defaults to lettersOnly for letters-portion sets (aToZ, unicodeLetters, custom); self-contained sets (binary, crockford, hexadecimal, base62, all) define their own complete alphabet.
  • Only codeType set → effective letter alphabet defaults to aToZ (when the type allows letters).

Compatibility (asserts at construction)

  • aToZ / unicodeLetters / custom require codeType ∈ {lettersOnly, numbersAndLetters, null}.
  • binary requires codeType ∈ {numbersOnly, null}.
  • crockford / hexadecimal / base62 require codeType ∈ {numbersAndLetters, null}.
  • base62 additionally requires codeLetterCasing ∈ {mixed, null}, since uppercase / lowercase casing would collapse the 62-char alphabet.
  • custom requires customSet to be non-empty.

Behavior

  • numberOfDigits — how many character boxes to show / how many characters the code has. Must be in [2, 50]. Defaults to 6.
  • autoFocus — whether the keyboard should pop up shortly after the widget is built (waits ~350ms so a route transition does not fight the keyboard appearing). Only checked at initState. Defaults to true.
  • onChanged — called every time the typed value changes, with the current sanitized text (0 to numberOfDigits chars; the exact set of characters depends on codeType and codeLetterSet).
  • showCountdown — whether the countdown line and expired view are shown at all. When false the field stays active forever and the countdown parameters are ignored. Defaults to true.
  • countdownDuration — how long the code is valid for, anchored at countdownStartDateTime. Defaults to 30 minutes.
  • showWrongCodeMessage — whether the wrong-code message is shown after onSubmit returns false. The field is still cleared and re-focused either way. Defaults to true.

Text

Containers and builders

  • countdownContainerBuilder — wraps the countdown text. Defaults to left alignment.
  • errorContainerBuilder — wraps the error messages. Defaults to left alignment.
  • digitContainerBuilder — wraps each digit box. Defaults to a 50x60 rounded rectangle with a 1px border that switches between the active and inactive border colors.
  • buttonBuilder — builds the "Retry" and "Go back to try again" buttons. When null, a plain elevated button is used.

Styles

  • digitTextStyle — typed digits (should include the digit color, as no separate color parameter exists).
  • errorTextStyle — error messages (should include the error color).
  • expiredTextStyle — "Your code expired" headline.
  • countdownTextStyle — countdown line (should include the desired color).
  • buttonTextStyle — shared by the "Retry" and "Go back" buttons. The color is overridden by buttonTextColor, so this style mostly contributes typography (size, weight, family).

Colors

Debug

  • debugShowTextField — when true, the hidden text field is shown as a normal visible field below the digit boxes so a developer can see what it holds and how the input formatters behave. Must be false in production. Defaults to false.
Inheritance
Available extensions

Constructors

OtpCodeVerificationField({int numberOfDigits = 6, OtpCodeType? codeType, OtpCodeLetterCasing? codeLetterCasing, OtpCodeLetterSet? codeLetterSet, String customSet = '', bool autoFocus = true, ValueChanged<String>? onChanged, required Future<bool> onSubmit(String code), bool showCountdown = true, required DateTime? countdownStartDateTime, Duration countdownDuration = const Duration(minutes: 30), required VoidCallback onExpiredGoBack, bool showWrongCodeMessage = true, String wrongCodeMessage = 'Enter the correct code', String verificationFailedMessageBuilder({Object? error}) = defaultVerificationFailedMessageBuilder, String retryButtonLabel = 'Retry', String expiredMessage = 'Your code expired', String expiredButtonLabel = 'Go back to try again', String countdownTextBuilder({required int secondsCountdown}) = defaultCountdownText, Widget countdownContainerBuilder({required Widget child}) = defaultCountdownContainerBuilder, Widget errorContainerBuilder({required Widget child}) = defaultErrorContainerBuilder, Widget digitContainerBuilder({required Color? activeBorderColor, required Color? backgroundColor, required Color? borderColor, required Widget child, required bool isCurrentDigit}) = defaultDigitContainerBuilder, Widget buttonBuilder({required Color? backgroundColor, required String label, required VoidCallback onTap, required Color? textColor, required TextStyle? textStyle})?, TextStyle? digitTextStyle, TextStyle? errorTextStyle, TextStyle? expiredTextStyle, TextStyle? countdownTextStyle, TextStyle? buttonTextStyle, Color? digitBackgroundColor, Color? digitBorderColor, Color? digitActiveBorderColor, Color? cursorColor, Size? cursorSize = const Size(3, 30), Color? buttonBackgroundColor, Color? buttonTextColor, bool debugShowTextField = false, Key? key})
const

Properties

autoFocus bool
Whether to automatically focus the field (showing the keyboard) shortly after the widget is first built. Defaults to true.
final
buttonBackgroundColor Color?
Background color of the "Go back to try again" button on the expired view, also used for the "Retry" button shown after a verification failure.
final
buttonBuilder Widget Function({required Color? backgroundColor, required String label, required VoidCallback onTap, required Color? textColor, required TextStyle? textStyle})?
Builder for the both the expired-button (shown when the countdown expires) and the retry-button (shown if onSubmit throws).
final
buttonTextColor Color?
Text/foreground color of the "Go back to try again" and "Retry" button labels. Applied on top of buttonTextStyle, so it wins over any color baked into that style.
final
buttonTextStyle TextStyle?
Text style used for both the "Go back to try again" button label on the expired view and the "Retry" button shown after a verification failure. The button's text color is overridden by buttonTextColor, so this style mostly contributes typography (size, weight, family).
final
codeLetterCasing OtpCodeLetterCasing?
How letters typed into the field are normalized (forced uppercase, forced lowercase, or kept as typed for mixed). null means "no normalization is applied". See OtpCodeLetterCasing and the "Defaults & inference" section in the class doc.
final
codeLetterSet OtpCodeLetterSet?
Which characters are accepted by the field. null means "not specified" — see OtpCodeLetterSet for the available alphabets and the "Defaults & inference" section in the class doc.
final
codeType OtpCodeType?
What kind of characters are accepted into the field: digits only, letters only, or both. null means "not specified" — see the "Defaults & inference" section in the class doc for what that means in combination with codeLetterSet / codeLetterCasing.
final
countdownContainerBuilder Widget Function({required Widget child})
Builds a container for the countdown text. Defaults to defaultCountdownContainerBuilder, which aligns to the left.
final
countdownDuration Duration
How long the verification code is valid for, anchored at countdownStartDateTime.
final
countdownStartDateTime DateTime?
Wall-clock time the verification code was issued at, used as the countdown anchor.
final
countdownTextBuilder String Function({required int secondsCountdown})
Builds the text shown below the digits while the countdown is running, from the remaining seconds. Defaults to defaultCountdownText, which formats as "Code valid for M:SS".
final
countdownTextStyle TextStyle?
Text style used for the "Code valid for M:SS" countdown line shown below the digits while the countdown is running.
final
cursorColor Color?
Color of the blinking cursor drawn inside the active (next-to-fill) digit box while the user is typing.
final
cursorSize Size?
Size of the blinking cursor drawn inside the active (next-to-fill) digit box while the user is typing. Defaults to Size(3, 30).
final
customSet String
When codeLetterSet is OtpCodeLetterSet.custom, this string is the allowed character set: only characters present in customSet can be typed (plus digits 0-9 when codeType is OtpCodeType.numbersAndLetters). Required to be non-empty in that mode; ignored otherwise. Defaults to ''.
final
debugShowTextField bool
Must be false for production. Debug aid: when true, the underlying TextField is rendered as a normal, visible textfield below the digit boxes (instead of being hidden behind them via the FittedBox/Stack hack), so a developer can see exactly what the textfield holds and how the input formatters behave. Defaults to false, which is the production layout — taps fall through the digit boxes onto the hidden textfield behind them.
final
digitActiveBorderColor Color?
Border color for the single digit box at the current cursor position (index typed.length). Used to visually highlight where the next digit will be entered.
final
digitBackgroundColor Color?
Fill color for each digit box (the rounded rectangle behind the digit / cursor / empty state).
final
digitBorderColor Color?
Border color for digit boxes that are NOT at the current cursor position (i.e. boxes other than the one at index typed.length).
final
digitContainerBuilder Widget Function({required Color? activeBorderColor, required Color? backgroundColor, required Color? borderColor, required Widget child, required bool isCurrentDigit})
Builds the container for each digit box, wrapping the digit content (typed digit, blinking cursor, or empty). Defaults to defaultDigitContainerBuilder, which renders a 50x60 rounded rectangle with a 1px border. The border uses activeBorderColor when isCurrentDigit is true (the box at the current cursor position) and borderColor otherwise.
final
digitTextStyle TextStyle?
Text style used for each typed digit displayed inside the digit boxes.
final
errorContainerBuilder Widget Function({required Widget child})
Builds a container for the error text (the "Enter the correct code" and "Could not verify the code" messages shown below the digits). Defaults to defaultErrorContainerBuilder, which aligns to the left.
final
errorTextStyle TextStyle?
Text style used for the error message shown below the digits — both the "Enter the correct code" message after a wrong-code attempt and the "Could not verify the code" message after a verification failure.
final
expiredButtonLabel String
Label of the button shown on the expired view. Defaults to 'Go back to try again'.
final
expiredMessage String
Headline shown on the expired view, above the onExpiredGoBack button. Defaults to 'Your code expired'.
final
expiredTextStyle TextStyle?
Text style used for the "Your code expired" headline on the expired view (shown above the onExpiredGoBack button after the countdown reaches 0).
final
hashCode int
The hash code for this object.
no setterinherited
key Key?
Controls how one widget replaces another widget in the tree.
finalinherited
makeRefreshable Widget

Available on Widget?, provided by the WidgetExtension extension

Make your any widget refreshable with RefreshIndicator on top
no setter
numberOfDigits int
How many digit boxes to show / how many digits the code has.
final
onChanged ValueChanged<String>?
Called every time the typed value changes.
final
onExpiredGoBack VoidCallback
Called when the user taps the "Go back to try again" button on the expired view.
final
onSubmit Future<bool> Function(String code)
Called once all numberOfDigits characters have been entered, with the typed code. The widget itself dismisses the keyboard for the duration of the onSubmit call.
final
retryButtonLabel String
Label of the button shown after onSubmit throws. Tapping it resubmits the same code via onSubmit again. Defaults to 'Retry'.
final
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
showCountdown bool
Whether the countdown timer (and its eventual "code expired" view) are shown at all.
final
showWrongCodeMessage bool
Whether the "Enter the correct code" message is shown below the digits after onSubmit returns false. Defaults to true.
final
verificationFailedMessageBuilder String Function({Object? error})
Builds the message shown below the digits when onSubmit throws.
final
wrongCodeMessage String
Message shown below the digits when onSubmit returns false. Defaults to 'Enter the correct code'.
final

Methods

addMaterialWidget() Material

Available on Widget, provided by the GenericExtensions extension

addTooltipWidget(String toolTip) Tooltip

Available on Widget, provided by the GenericExtensions extension

animate({Key? key, List<Effect>? effects, AnimateCallback? onInit, AnimateCallback? onPlay, AnimateCallback? onComplete, bool? autoPlay, Duration? delay, AnimationController? controller, Adapter? adapter, double? target, double? value}) Animate

Available on Widget, provided by the AnimateWidgetExtensions extension

Wraps the target Widget in an Animate instance, and returns the instance for chaining calls. Ex. myWidget.animate() is equivalent to Animate(child: myWidget).
borderRadius([BorderRadiusGeometry? borderRadius]) Widget

Available on Widget, provided by the GenericExtensions extension

boxDecoration([BoxDecoration? boxDecoration]) Widget

Available on Widget, provided by the GenericExtensions extension

center({double? heightFactor, double? widthFactor}) Widget

Available on Widget?, provided by the WidgetExtension extension

set parent widget in center
colorFilter([ColorFilter? colorFilter]) Widget

Available on Widget, provided by the GenericExtensions extension

set parent widget in center
cornerRadiusWithClipRRect(double radius) ClipRRect

Available on Widget?, provided by the WidgetExtension extension

add corner radius
cornerRadiusWithClipRRectOnly({int bottomLeft = 0, int bottomRight = 0, int topLeft = 0, int topRight = 0}) ClipRRect

Available on Widget?, provided by the WidgetExtension extension

add custom corner radius each side
createElement() StatefulElement
Creates a StatefulElement to manage this widget's location in the tree.
inherited
createState() State<OtpCodeVerificationField>
Creates the mutable state for this widget at a given location in the tree.
override
debugDescribeChildren() List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
inherited
expand({int flex = 1}) Widget

Available on Widget?, provided by the WidgetExtension extension

add Expanded to parent widget
fit({BoxFit? fit, AlignmentGeometry? alignment}) Widget

Available on Widget?, provided by the WidgetExtension extension

add FittedBox to parent widget
flexible({int flex = 1, FlexFit? fit}) Widget

Available on Widget?, provided by the WidgetExtension extension

add Flexible to parent widget
launch<T>(BuildContext context, {bool isNewTask = false, PageRouteAnimation? pageRouteAnimation, Duration? duration, String? routeName, Object? routeArguments}) Future<T?>

Available on Widget?, provided by the WidgetExtension extension

Launch a new screen
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onTap(Function? function, {BorderRadius? borderRadius, Color? splashColor, Color? hoverColor, Color? highlightColor, Color? focusColor, WidgetStateProperty<Color?>? overlayColor}) Widget

Available on Widget?, provided by the WidgetExtension extension

add tap to parent widget
opacity({required double opacity, int durationInSecond = 1, Duration? duration}) Widget

Available on Widget?, provided by the WidgetExtension extension

add opacity to parent widget
paddingAll(double padding) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding all
paddingBottom(double bottom) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding bottom
paddingDirectional({double start = 0.0, double top = 0.0, double end = 0.0, double bottom = 0.0}) Widget

Available on Widget?, provided by the WidgetExtension extension

paddingLeft(double left) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding left
paddingOnly({double top = 0.0, double left = 0.0, double bottom = 0.0, double right = 0.0}) Padding

Available on Widget?, provided by the WidgetExtension extension

return custom padding from each side
paddingRight(double right) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding right
paddingSymmetric({double vertical = 0.0, double horizontal = 0.0}) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding symmetric
paddingTop(double top) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding top
rotate({required double angle, bool transformHitTests = true, Offset? origin}) Widget

Available on Widget?, provided by the WidgetExtension extension

add rotation to parent widget
scale({required double scale, Offset? origin, AlignmentGeometry? alignment, bool transformHitTests = true}) Widget

Available on Widget?, provided by the WidgetExtension extension

add scaling to parent widget
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
tooltip({required String msg}) Widget

Available on Widget?, provided by the WidgetExtension extension

toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) String
Returns a one-line detailed description of the object.
inherited
toStringShort() String
A short, textual description of this widget.
inherited
translate({required Offset offset, bool transformHitTests = true, Key? key}) Widget

Available on Widget?, provided by the WidgetExtension extension

add translate to parent widget
validate({Widget value = const SizedBox()}) Widget

Available on Widget?, provided by the WidgetExtension extension

Validate given widget is not null and returns given value if null.
visible(bool visible, {Widget? defaultWidget}) Widget

Available on Widget?, provided by the WidgetExtension extension

set visibility
withHeight(double height) SizedBox

Available on Widget?, provided by the WidgetExtension extension

With custom height
withRoundedCorners({Color backgroundColor = whiteColor, BorderRadius borderRadius = const BorderRadius.all(Radius.circular(8.0)), LinearGradient? gradient, BoxBorder? border, List<BoxShadow>? boxShadow, DecorationImage? decorationImage, BoxShape boxShape = BoxShape.rectangle}) Container

Available on Widget?, provided by the WidgetExtension extension

withScroll({ScrollPhysics? physics, EdgeInsetsGeometry? padding, Axis scrollDirection = Axis.vertical, ScrollController? controller, DragStartBehavior dragStartBehavior = DragStartBehavior.start, bool? primary, required bool reverse}) Widget

Available on Widget?, provided by the WidgetExtension extension

withShaderMask(List<Color> colors, {BlendMode blendMode = BlendMode.srcATop}) Widget

Available on Widget?, provided by the WidgetExtension extension

Wrap with ShaderMask widget
withShaderMaskGradient(Gradient gradient, {BlendMode blendMode = BlendMode.srcATop}) Widget

Available on Widget?, provided by the WidgetExtension extension

Wrap with ShaderMask widget Gradient
withShadow({Color bgColor = whiteColor, Color shadowColor = Colors.black12, dynamic blurRadius = 10.0, dynamic spreadRadius = 0.0, Offset offset = const Offset(0.0, 0.0), LinearGradient? gradient, BoxBorder? border, DecorationImage? decorationImage, BoxShape boxShape = BoxShape.rectangle}) Container

Available on Widget?, provided by the WidgetExtension extension

withSize({double width = 0.0, double height = 0.0}) SizedBox

Available on Widget?, provided by the WidgetExtension extension

With custom height and width
withTooltip({required String msg}) Widget

Available on Widget?, provided by the WidgetExtension extension

Validate given widget is not null and returns given value if null.
withVisibility(bool visible, {Widget? replacement, bool maintainAnimation = false, bool maintainState = false, bool maintainSize = false, bool maintainSemantics = false, bool maintainInteractivity = false}) Visibility

Available on Widget?, provided by the WidgetExtension extension

set widget visibility
withWidth(double width) SizedBox

Available on Widget?, provided by the WidgetExtension extension

With custom width

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Methods

defaultCountdownContainerBuilder({required Widget child}) Widget
Default formatter for countdownContainerBuilder: simply left-aligns the text.
defaultCountdownText({required int secondsCountdown}) String
Default formatter for countdownTextBuilder: produces "Code valid for M:SS", where the seconds are zero-padded to 2 digits and the minutes are not padded.
defaultDigitContainerBuilder({required Color? backgroundColor, required bool isCurrentDigit, required Color? activeBorderColor, required Color? borderColor, required Widget child}) Widget
Default builder for digitContainerBuilder: a 50x60 rounded rectangle with a 1px border that switches between activeBorderColor and borderColor depending on isCurrentDigit.
defaultErrorContainerBuilder({required Widget child}) Widget
Default formatter for errorContainerBuilder: simply left-aligns the text.
defaultVerificationFailedMessageBuilder({Object? error}) String
Default builder for verificationFailedMessageBuilder: returns "Could not verify the code" when errorMessage is null or empty, and "Could not verify the code: $errorMessage" otherwise.