premium_otp_input 0.0.4 copy "premium_otp_input: ^0.0.4" to clipboard
premium_otp_input: ^0.0.4 copied to clipboard

A highly customizable, beautiful, and interactive OTP and PIN entry widget for Flutter featuring rich animations, verification loaders, and secure masking.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:premium_otp_input/premium_otp_input.dart';

void main() {
  runApp(const MyApp());
}

/// The main application widget for the example.
class MyApp extends StatelessWidget {
  /// Creates the [MyApp] widget.
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Premium OTP Input Demo',
      theme: ThemeData.dark(useMaterial3: true).copyWith(
        scaffoldBackgroundColor: const Color(0xFF0F172A),
        colorScheme: const ColorScheme.dark(
          primary: Color(0xFFF97316),
          surface: Color(0xFF1E293B),
        ),
      ),
      home: const OtpDemoScreen(),
    );
  }
}

/// A screen showcasing the different interactive features of the [PremiumOtpInput] widget.
class OtpDemoScreen extends StatefulWidget {
  /// Creates the [OtpDemoScreen] widget.
  const OtpDemoScreen({super.key});

  @override
  State<OtpDemoScreen> createState() => _OtpDemoScreenState();
}

class _OtpDemoScreenState extends State<OtpDemoScreen> {
  final TextEditingController _otpController = TextEditingController();
  final FocusNode _otpFocusNode = FocusNode();

  bool _isVerifying = false;
  bool _isSuccess = false;
  bool _isError = false;

  OtpEntryAnimationStyle _entryAnimationStyle = OtpEntryAnimationStyle.scale;
  OtpSuccessAnimationStyle _successAnimationStyle = OtpSuccessAnimationStyle.bounce;
  bool _obscureText = false;
  String _obscuringCharacter = '●';

  void _handleOtpCompleted(String value) async {
    setState(() {
      _isVerifying = true;
      _isError = false;
    });

    // Simulate verification delay
    await Future.delayed(const Duration(milliseconds: 1500));

    if (!mounted) return;

    if (value == "123456") {
      setState(() {
        _isVerifying = false;
        _isSuccess = true;
      });
    } else {
      setState(() {
        _isVerifying = false;
        _isError = true;
      });
      // Automatically clear the error state after 2 seconds
      await Future.delayed(const Duration(seconds: 2));
      if (!mounted) return;
      setState(() {
        _isError = false;
        _otpController.clear();
        _otpFocusNode.requestFocus();
      });
    }
  }

  void _reset() {
    setState(() {
      _isVerifying = false;
      _isSuccess = false;
      _isError = false;
      _otpController.clear();
      _otpFocusNode.requestFocus();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Premium OTP Input'),
        backgroundColor: Colors.transparent,
        elevation: 0,
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              // 1. Info / Status banner
              Card(
                color: const Color(0xFF1E293B),
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      const Text(
                        'Interactive Demo App',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                          color: Color(0xFFF97316),
                        ),
                      ),
                      const SizedBox(height: 8),
                      Text(
                        _isSuccess
                            ? 'Verification Successful!'
                            : _isError
                                ? 'Verification Failed (Try again)'
                                : _isVerifying
                                    ? 'Verifying code...'
                                    : 'Enter "123456" for success, any other code for error.',
                        textAlign: TextAlign.center,
                        style: TextStyle(
                          color: _isSuccess
                              ? const Color(0xFF22C55E)
                              : _isError
                                  ? const Color(0xFFEF5350)
                                  : Colors.white70,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 32),

              // 2. The OTP Input component
              PremiumOtpInput(
                length: 6,
                controller: _otpController,
                focusNode: _otpFocusNode,
                isSuccess: _isSuccess,
                isError: _isError,
                isVerifying: _isVerifying,
                onCompleted: _handleOtpCompleted,
                obscureText: _obscureText,
                obscuringCharacter: _obscuringCharacter,
                entryAnimationStyle: _entryAnimationStyle,
                successAnimationStyle: _successAnimationStyle,
                animateActiveBorder: true,
                boxHeight: 64.0,
                spacing: 12.0,
                borderRadius: 16.0,
                activeBorderColor: const Color(0xFFF97316),
                defaultBorderColor: Colors.white.withValues(alpha: 0.12),
                successColor: const Color(0xFF22C55E),
                errorColor: const Color(0xFFEF5350),
              ),
              const SizedBox(height: 32),

              // 3. Actions / Controls
              if (_isSuccess || _isError || _isVerifying)
                Center(
                  child: TextButton.icon(
                    onPressed: _reset,
                    icon: const Icon(Icons.refresh),
                    label: const Text('Reset Input'),
                    style: TextButton.styleFrom(
                      foregroundColor: const Color(0xFFF97316),
                    ),
                  ),
                ),
              const SizedBox(height: 24),

              // 4. Configuration Controls Card
              Card(
                color: const Color(0xFF1E293B),
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Customize Settings',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                        ),
                      ),
                      const Divider(height: 24, color: Colors.white10),

                      // Entry Animation Style
                      const Text('Digit Entry Animation', style: TextStyle(color: Colors.white70)),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8.0,
                        children: OtpEntryAnimationStyle.values.map((style) {
                          return ChoiceChip(
                            label: Text(style.name),
                            selected: _entryAnimationStyle == style,
                            onSelected: (selected) {
                              if (selected) {
                                setState(() => _entryAnimationStyle = style);
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 20),

                      // Success Animation Style
                      const Text('Success State Transition', style: TextStyle(color: Colors.white70)),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8.0,
                        children: OtpSuccessAnimationStyle.values.map((style) {
                          return ChoiceChip(
                            label: Text(style.name),
                            selected: _successAnimationStyle == style,
                            onSelected: (selected) {
                              if (selected) {
                                setState(() => _successAnimationStyle = style);
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 20),

                      // Obscure text & character selection
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          const Text('Obscure / Hide Text', style: TextStyle(color: Colors.white70)),
                          Switch(
                            value: _obscureText,
                            activeThumbColor: const Color(0xFFF97316),
                            onChanged: (val) {
                              setState(() => _obscureText = val);
                            },
                          ),
                        ],
                      ),
                      if (_obscureText) ...[
                        const SizedBox(height: 12),
                        const Text('Obscuring Character', style: TextStyle(color: Colors.white70)),
                        const SizedBox(height: 8),
                        Wrap(
                          spacing: 12.0,
                          children: ['●', '*', '★', '♥'].map((char) {
                            return ChoiceChip(
                              label: Text(char),
                              selected: _obscuringCharacter == char,
                              onSelected: (selected) {
                                if (selected) {
                                  setState(() => _obscuringCharacter = char);
                                }
                              },
                            );
                          }).toList(),
                        ),
                      ],
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
1
likes
160
points
39
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A highly customizable, beautiful, and interactive OTP and PIN entry widget for Flutter featuring rich animations, verification loaders, and secure masking.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, google_fonts

More

Packages that depend on premium_otp_input