otp_verification_flutter
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 (idle → verifying → success / error).
Inspired by the "OTP Verification Deck" design (4 boxes, glow on focus, a loading screen, then a green confirmation).
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