otp_verification_flutter

License Pub Version GitHub stars

An animated OTP verification widget for Flutter — individual boxes with auto-focus / auto-advance, native autofill (SMS on Android / QuickType on iOS), and a full state-driven screen (idleverifyingsuccess / error).

Inspired by the "OTP Verification Deck" design (4 boxes, glow on focus, a loading screen, then a green confirmation).

Demo

otp_verification_flutter demo

Installation

dependencies:
  otp_verification_flutter: ^0.1.2

Quick start

final controller = OtpController(length: 4); // configurable length

OtpVerificationWidget(
  controller: controller,
  theme: OtpFieldTheme.darkGlow, // or .lightMinimal, or your own theme
  subtitleBuilder: (context) => const Text('We texted a code to +1 415 •••0142.'),
  onCompleted: (code) async {
    controller.setState(OtpVerificationState.verifying);
    final ok = await myApi.verify(code);
    controller.setState(
      ok ? OtpVerificationState.success : OtpVerificationState.error,
    );
  },
  onResend: () => controller.reset(),
  onContinue: () => Navigator.pushReplacementNamed(context, '/home'),
);

Just the boxes (without the full screen)

If you only want the OTP boxes inside your own layout:

OtpField(
  controller: controller,
  theme: OtpFieldTheme.darkGlow,
  onCompleted: (code) => print(code),
);

Customization

Everything is driven by OtpFieldTheme — colors, corner radius, box size, border width, fill animation duration/curve, focus glow, and the automatic shake on error:

final theme = OtpFieldTheme.darkGlow.copyWith(
  filledBorderColor: Colors.purpleAccent,
  borderRadius: 20,
  size: 64,
  mergeDuration: const Duration(milliseconds: 1800), // boxes merge animation
);

OtpVerificationWidget text and styles

Every piece of text shown by the full screen is a parameter, with its current default value:

Parameter Default
title 'Enter your code'
verifyingTitle 'Checking your code'
verifyingSubtitle "One moment — we're confirming it."
successTitle 'Verified'
successSubtitle 'Setting up your session…'
continueLabel 'Continue'
resendLabel 'Resend'
didntGetCodeLabel "Didn't get a code? "

The Continue button and the Resend link are fully styleable. Without an override, the button uses theme.successColor and the link uses theme.focusedBorderColor.

OtpVerificationWidget(
  controller: controller,

  // Override a text
  continueLabel: 'Go to my account',
  didntGetCodeLabel: "Didn't receive the code? ",
  resendLabel: 'Resend',

  // Full style of the "Continue" button
  continueButtonStyle: FilledButton.styleFrom(
    backgroundColor: Colors.indigo,
    foregroundColor: Colors.white,
    padding: const EdgeInsets.symmetric(vertical: 16),
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(28),
    ),
  ),
  continueButtonTextStyle: const TextStyle(
    fontSize: 16,
    fontWeight: FontWeight.w700,
  ),

  // "Resend" link style (clickable part and static part styled separately)
  resendLabelStyle: const TextStyle(
    color: Colors.indigoAccent,
    fontWeight: FontWeight.w700,
    fontSize: 14,
  ),
  didntGetCodeStyle: const TextStyle(color: Colors.white38, fontSize: 14),
);

Native autofill

enableAutofill: true (the default) wires AutofillHints.oneTimeCode onto the first box, which enables:

  • Android: the SMS Retriever / keyboard autofill framework
  • iOS: the QuickType suggestion above the keyboard

No extra native configuration is required — it is handled by Flutter's TextField.autofillHints API.

Package structure

lib/
  otp_verification_flutter.dart   // public exports
  src/
    otp_controller.dart           // state + focus + code
    otp_field.dart                // row of animated boxes
    otp_merge_view.dart           // boxes -> card -> loader -> check/error
    otp_verification_widget.dart  // full screen (title + boxes + resend + continue)
    otp_state.dart                // enum idle/verifying/success/error
    otp_theme.dart                // theme + presets
example/
  lib/main.dart                   // full demo

License

MIT © Franck Dabryn Siyapze