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

A highly customizable, beautiful, and interactive OTP and PIN entry widget for Flutter featuring Sigil, Nexus, Lightning, Liquid, and Motion animations.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.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 StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isGlobalDark = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Premium OTP Input Showcase',
      debugShowCheckedModeBanner: false,
      themeMode: _isGlobalDark ? ThemeMode.dark : ThemeMode.light,
      theme: ThemeData.light(useMaterial3: true).copyWith(
        scaffoldBackgroundColor: const Color(0xFFF8FAFC),
        colorScheme: const ColorScheme.light(
          primary: Color(0xFF4F6EF7),
          surface: Colors.white,
        ),
      ),
      darkTheme: ThemeData.dark(useMaterial3: true).copyWith(
        scaffoldBackgroundColor: const Color(0xFF0B0F19),
        colorScheme: const ColorScheme.dark(
          primary: Color(0xFF6366F1),
          surface: Color(0xFF1E293B),
        ),
      ),
      home: OtpDemoScreen(
        isGlobalDark: _isGlobalDark,
        onToggleGlobalTheme: () {
          setState(() {
            _isGlobalDark = !_isGlobalDark;
          });
        },
      ),
    );
  }
}

enum DemoStyle { nexus, sigil, liquid, lightning, motion, standard }

class OtpDemoScreen extends StatefulWidget {
  final bool isGlobalDark;
  final VoidCallback onToggleGlobalTheme;

  const OtpDemoScreen({
    super.key,
    required this.isGlobalDark,
    required this.onToggleGlobalTheme,
  });

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

class _OtpDemoScreenState extends State<OtpDemoScreen> {
  // Controllers and Focus Nodes
  final TextEditingController _sigilController = TextEditingController();
  final FocusNode _sigilFocusNode = FocusNode();

  final TextEditingController _liquidController = TextEditingController();
  final FocusNode _liquidFocusNode = FocusNode();

  final TextEditingController _lightningController = TextEditingController();
  final FocusNode _lightningFocusNode = FocusNode();

  final TextEditingController _motionController = TextEditingController();
  final FocusNode _motionFocusNode = FocusNode();

  final TextEditingController _nexusController = TextEditingController();
  final FocusNode _nexusFocusNode = FocusNode();

  final TextEditingController _standardController = TextEditingController();
  final FocusNode _standardFocusNode = FocusNode();

  DemoStyle _currentStyle = DemoStyle.nexus;

  // Widget States
  bool _isVerifying = false;
  bool _isSuccess = false;
  bool _isError = false;

  // Per-widget dark overrides (null = sync with global theme)
  bool? _widgetDarkOverride;

  // Standard Customization Options
  OtpEntryAnimationStyle _entryAnimationStyle = OtpEntryAnimationStyle.scale;
  OtpSuccessAnimationStyle _successAnimationStyle =
      OtpSuccessAnimationStyle.bounce;
  bool _obscureText = false;
  String _obscuringCharacter = '●';
  double _boxHeight = 64.0;
  double _borderRadius = 16.0;

  bool get _isEffectiveDark => _widgetDarkOverride ?? widget.isGlobalDark;

  @override
  void dispose() {
    _sigilController.dispose();
    _sigilFocusNode.dispose();
    _liquidController.dispose();
    _liquidFocusNode.dispose();
    _lightningController.dispose();
    _lightningFocusNode.dispose();
    _motionController.dispose();
    _motionFocusNode.dispose();
    _nexusController.dispose();
    _nexusFocusNode.dispose();
    _standardController.dispose();
    _standardFocusNode.dispose();
    super.dispose();
  }

  void _clearAllInput() {
    setState(() {
      _isVerifying = false;
      _isSuccess = false;
      _isError = false;
      _sigilController.clear();
      _liquidController.clear();
      _lightningController.clear();
      _motionController.clear();
      _nexusController.clear();
      _standardController.clear();
    });
    _focusActiveNode();
  }

  void _focusActiveNode() {
    switch (_currentStyle) {
      case DemoStyle.sigil:
        _sigilFocusNode.requestFocus();
      case DemoStyle.liquid:
        _liquidFocusNode.requestFocus();
      case DemoStyle.lightning:
        _lightningFocusNode.requestFocus();
      case DemoStyle.motion:
        _motionFocusNode.requestFocus();
      case DemoStyle.nexus:
        _nexusFocusNode.requestFocus();
      case DemoStyle.standard:
        _standardFocusNode.requestFocus();
    }
  }

  void _simulateCompletion(String code, String targetSuccessCode) async {
    setState(() {
      _isVerifying = true;
      _isError = false;
    });

    await Future.delayed(const Duration(milliseconds: 1400));

    if (!mounted) return;

    if (code == targetSuccessCode) {
      setState(() {
        _isVerifying = false;
        _isSuccess = true;
      });
    } else {
      setState(() {
        _isVerifying = false;
        _isError = true;
      });
      await Future.delayed(const Duration(seconds: 2));
      if (!mounted) return;
      setState(() {
        _isError = false;
      });
      _clearAllInput();
    }
  }



  @override
  Widget build(BuildContext context) {
    final isDark = widget.isGlobalDark;
    final bg = isDark ? const Color(0xFF0B0F19) : const Color(0xFFF1F5F9);
    final cardBg = isDark ? const Color(0xFF131C2E) : Colors.white;
    final textPrimary = isDark ? Colors.white : const Color(0xFF0F172A);
    final textSecondary = isDark ? const Color(0xFF94A3B8) : const Color(0xFF64748B);

    return Scaffold(
      backgroundColor: bg,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0,
        centerTitle: false,
        title: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  colors: [Color(0xFF6366F1), Color(0xFFA855F7)],
                ),
                borderRadius: BorderRadius.circular(12),
              ),
              child: const Icon(Icons.shield_outlined, color: Colors.white, size: 20),
            ),
            const SizedBox(width: 12),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Premium OTP Input',
                  style: GoogleFonts.outfit(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                    color: textPrimary,
                  ),
                ),
                Text(
                  'v1.2.0 β€’ Ultra Creative Flutter Suite',
                  style: GoogleFonts.outfit(
                    fontSize: 11,
                    fontWeight: FontWeight.w500,
                    color: textSecondary,
                  ),
                ),
              ],
            ),
          ],
        ),
        actions: [
          IconButton(
            tooltip: isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode',
            icon: Icon(
              isDark ? Icons.wb_sunny_rounded : Icons.nightlight_round,
              color: isDark ? const Color(0xFFF59E0B) : const Color(0xFF6366F1),
            ),
            onPressed: widget.onToggleGlobalTheme,
          ),
          const SizedBox(width: 12),
        ],
      ),
      body: SingleChildScrollView(
        physics: const BouncingScrollPhysics(),
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
        child: Center(
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 800),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                // 1. Creative Header & Hero Badge
                _buildHeroBanner(isDark, cardBg, textPrimary, textSecondary),
                const SizedBox(height: 20),

                // 2. Style Selector Tabs (Sigil, Liquid, Lightning, Motion, Nexus, Standard)
                _buildStyleSelector(isDark),
                const SizedBox(height: 24),


                // 4. Main Active OTP Input Container Card
                _buildActiveOtpShowcaseCard(isDark, cardBg),
                const SizedBox(height: 24),

                // 5. Customization Options (Only for Standard mode or global overrides)
                if (_currentStyle == DemoStyle.standard) ...[
                  _buildStandardCustomizer(isDark, cardBg, textPrimary, textSecondary),
                  const SizedBox(height: 24),
                ],

                const SizedBox(height: 32),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildHeroBanner(
    bool isDark,
    Color cardBg,
    Color textPrimary,
    Color textSecondary,
  ) {
    final styleInfo = _getStyleInfo(_currentStyle);

    return Container(
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: cardBg,
        borderRadius: BorderRadius.circular(24),
        border: Border.all(
          color: isDark
              ? Colors.white.withValues(alpha: 0.08)
              : const Color(0xFFE2E8F0),
        ),
        boxShadow: [
          BoxShadow(
            color: styleInfo.accentColor.withValues(alpha: 0.12),
            blurRadius: 24,
            spreadRadius: -4,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Row(
        children: [
          Container(
            padding: const EdgeInsets.all(14),
            decoration: BoxDecoration(
              color: styleInfo.accentColor.withValues(alpha: 0.15),
              borderRadius: BorderRadius.circular(18),
            ),
            child: Icon(styleInfo.icon, color: styleInfo.accentColor, size: 28),
          ),
          const SizedBox(width: 16),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Wrap(
                  spacing: 8,
                  runSpacing: 6,
                  crossAxisAlignment: WrapCrossAlignment.center,
                  children: [
                    Text(
                      styleInfo.name,
                      style: GoogleFonts.outfit(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                        color: textPrimary,
                      ),
                    ),
                    Container(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 8,
                        vertical: 3,
                      ),
                      decoration: BoxDecoration(
                        color: styleInfo.accentColor.withValues(alpha: 0.2),
                        borderRadius: BorderRadius.circular(8),
                      ),
                      child: Text(
                        styleInfo.tag,
                        style: GoogleFonts.outfit(
                          fontSize: 10,
                          fontWeight: FontWeight.bold,
                          color: styleInfo.accentColor,
                        ),
                      ),
                    ),
                    Container(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 8,
                        vertical: 3,
                      ),
                      decoration: BoxDecoration(
                        color: const Color(0xFF22C55E).withValues(alpha: 0.15),
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(
                          color: const Color(0xFF22C55E).withValues(alpha: 0.4),
                        ),
                      ),
                      child: Text(
                        'πŸ”‘ Code: ${styleInfo.correctCode}',
                        style: GoogleFonts.outfit(
                          fontSize: 11,
                          fontWeight: FontWeight.bold,
                          color: const Color(0xFF22C55E),
                        ),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 4),
                Text(
                  styleInfo.description,
                  style: GoogleFonts.outfit(
                    fontSize: 13,
                    color: textSecondary,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildStyleSelector(bool isDark) {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      physics: const BouncingScrollPhysics(),
      child: Row(
        children: DemoStyle.values.map((style) {
          final isSelected = _currentStyle == style;
          final info = _getStyleInfo(style);

          return Padding(
            padding: const EdgeInsets.only(right: 8.0),
            child: AnimatedContainer(
              duration: const Duration(milliseconds: 250),
              child: InkWell(
                onTap: () {
                  setState(() {
                    _currentStyle = style;
                    _widgetDarkOverride = null;
                    _clearAllInput();
                  });
                },
                borderRadius: BorderRadius.circular(16),
                child: Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 16,
                    vertical: 12,
                  ),
                  decoration: BoxDecoration(
                    color: isSelected
                        ? info.accentColor
                        : (isDark
                              ? const Color(0xFF1E293B)
                              : Colors.white),
                    borderRadius: BorderRadius.circular(16),
                    border: Border.all(
                      color: isSelected
                          ? info.accentColor
                          : (isDark
                                ? Colors.white.withValues(alpha: 0.08)
                                : const Color(0xFFE2E8F0)),
                    ),
                    boxShadow: isSelected
                        ? [
                            BoxShadow(
                              color: info.accentColor.withValues(alpha: 0.35),
                              blurRadius: 12,
                              offset: const Offset(0, 4),
                            )
                          ]
                        : null,
                  ),
                  child: Row(
                    children: [
                      Icon(
                        info.icon,
                        size: 16,
                        color: isSelected
                            ? Colors.white
                            : (isDark ? const Color(0xFF94A3B8) : const Color(0xFF64748B)),
                      ),
                      const SizedBox(width: 8),
                      Text(
                        info.shortName,
                        style: GoogleFonts.outfit(
                          fontSize: 13,
                          fontWeight:
                              isSelected ? FontWeight.bold : FontWeight.w600,
                          color: isSelected
                              ? Colors.white
                              : (isDark ? Colors.white : const Color(0xFF334155)),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          );
        }).toList(),
      ),
    );
  }



  Widget _buildActiveOtpShowcaseCard(bool isDark, Color cardBg) {
    return AnimatedSwitcher(
      duration: const Duration(milliseconds: 350),
      child: switch (_currentStyle) {
        DemoStyle.sigil => SigilOtpVerificationView(
            key: ValueKey('sigil_$_isEffectiveDark'),
            length: 4,
            isDark: _isEffectiveDark,
            controller: _sigilController,
            focusNode: _sigilFocusNode,
            isSuccess: _isSuccess,
            isError: _isError,
            isVerifying: _isVerifying,
            onCompleted: (val) => _simulateCompletion(val, '7890'),
            onResend: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('✨ Resent Sigil OTP Code (Try "7890")'),
                  backgroundColor: Color(0xFFA855F7),
                ),
              );
            },
          ),
        DemoStyle.liquid => LiquidOtpVerificationView(
            key: ValueKey('liquid_$_isEffectiveDark'),
            length: 4,
            isDark: _isEffectiveDark,
            controller: _liquidController,
            focusNode: _liquidFocusNode,
            isSuccess: _isSuccess,
            isError: _isError,
            isVerifying: _isVerifying,
            onCompleted: (val) => _simulateCompletion(val, '1234'),
            onResend: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('🌊 Resent Liquid OTP Code (Try "1234")'),
                  backgroundColor: Color(0xFF3B82F6),
                ),
              );
            },
          ),
        DemoStyle.lightning => LightningOtpVerificationView(
            key: ValueKey('lightning_$_isEffectiveDark'),
            length: 4,
            isDark: _isEffectiveDark,
            controller: _lightningController,
            focusNode: _lightningFocusNode,
            isSuccess: _isSuccess,
            isError: _isError,
            isVerifying: _isVerifying,
            onCompleted: (val) => _simulateCompletion(val, '4565'),
            onResend: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('⚑ Resent Lightning OTP Code (Try "4565")'),
                  backgroundColor: Color(0xFFF59E0B),
                ),
              );
            },
          ),
        DemoStyle.motion => MotionOtpVerificationView(
            key: ValueKey('motion_$_isEffectiveDark'),
            length: 4,
            isDark: _isEffectiveDark,
            controller: _motionController,
            focusNode: _motionFocusNode,
            isSuccess: _isSuccess,
            isError: _isError,
            isVerifying: _isVerifying,
            onCompleted: (val) => _simulateCompletion(val, '1234'),
            onResend: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('πŸŒ€ Resent Motion OTP Code (Try "1234")'),
                  backgroundColor: Color(0xFF6366F1),
                ),
              );
            },
          ),
        DemoStyle.nexus => NexusOtpVerificationView(
            key: ValueKey('nexus_$_isEffectiveDark'),
            length: 4,
            isDark: _isEffectiveDark,
            controller: _nexusController,
            focusNode: _nexusFocusNode,
            isSuccess: _isSuccess,
            isError: _isError,
            isVerifying: _isVerifying,
            onCompleted: (val) => _simulateCompletion(val, '4545'),
            onResend: () {
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(
                  content: Text('πŸ•ΈοΈ Resent Nexus OTP Code (Try "4545")'),
                  backgroundColor: Color(0xFFFF453A),
                ),
              );
            },
          ),
        DemoStyle.standard => Container(
            padding: const EdgeInsets.all(28),
            decoration: BoxDecoration(
              color: _isEffectiveDark ? const Color(0xFF1E293B) : Colors.white,
              borderRadius: BorderRadius.circular(24),
              border: Border.all(
                color: _isEffectiveDark
                    ? Colors.white.withValues(alpha: 0.12)
                    : const Color(0xFFE2E8F0),
              ),
            ),
            child: Column(
              children: [
                Text(
                  'Standard Premium OTP',
                  style: GoogleFonts.outfit(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    color: _isEffectiveDark ? Colors.white : const Color(0xFF0F172A),
                  ),
                ),
                const SizedBox(height: 6),
                Text(
                  'Fully configurable standard input widget',
                  style: GoogleFonts.outfit(
                    fontSize: 13,
                    color: _isEffectiveDark
                        ? const Color(0xFF94A3B8)
                        : const Color(0xFF64748B),
                  ),
                ),
                const SizedBox(height: 28),
                PremiumOtpInput(
                  length: 6,
                  controller: _standardController,
                  focusNode: _standardFocusNode,
                  isSuccess: _isSuccess,
                  isError: _isError,
                  isVerifying: _isVerifying,
                  onCompleted: (val) => _simulateCompletion(val, '123456'),
                  obscureText: _obscureText,
                  obscuringCharacter: _obscuringCharacter,
                  entryAnimationStyle: _entryAnimationStyle,
                  successAnimationStyle: _successAnimationStyle,
                  animateActiveBorder: true,
                  boxHeight: _boxHeight,
                  spacing: 10.0,
                  borderRadius: _borderRadius,
                  activeBorderColor: const Color(0xFF4F6EF7),
                  defaultBorderColor: _isEffectiveDark
                      ? Colors.white.withValues(alpha: 0.2)
                      : const Color(0xFFCBD5E1),
                  boxBackgroundColor: _isEffectiveDark
                      ? const Color(0xFF0F172A)
                      : const Color(0xFFF8FAFC),
                  textStyle: GoogleFonts.outfit(
                    fontSize: 24,
                    fontWeight: FontWeight.bold,
                    color: _isEffectiveDark ? Colors.white : const Color(0xFF0F172A),
                  ),
                ),
              ],
            ),
          ),
      },
    );
  }

  Widget _buildStandardCustomizer(
    bool isDark,
    Color cardBg,
    Color textPrimary,
    Color textSecondary,
  ) {
    return Container(
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: cardBg,
        borderRadius: BorderRadius.circular(20),
        border: Border.all(
          color: isDark
              ? Colors.white.withValues(alpha: 0.08)
              : const Color(0xFFE2E8F0),
        ),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              const Icon(Icons.tune_rounded, size: 20, color: Color(0xFF4F6EF7)),
              const SizedBox(width: 10),
              Text(
                'Customize Standard Properties',
                style: GoogleFonts.outfit(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                  color: textPrimary,
                ),
              ),
            ],
          ),
          const Divider(height: 24),

          // Entry animation style
          Text(
            'Digit Entry Animation:',
            style: GoogleFonts.outfit(
                fontSize: 13, fontWeight: FontWeight.w600, color: textSecondary),
          ),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            children: OtpEntryAnimationStyle.values.map((s) {
              return ChoiceChip(
                label: Text(s.name),
                selected: _entryAnimationStyle == s,
                onSelected: (sel) {
                  if (sel) setState(() => _entryAnimationStyle = s);
                },
              );
            }).toList(),
          ),
          const SizedBox(height: 16),

          // Success animation style
          Text(
            'Success Animation Style:',
            style: GoogleFonts.outfit(
                fontSize: 13, fontWeight: FontWeight.w600, color: textSecondary),
          ),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            children: OtpSuccessAnimationStyle.values.map((s) {
              return ChoiceChip(
                label: Text(s.name),
                selected: _successAnimationStyle == s,
                onSelected: (sel) {
                  if (sel) setState(() => _successAnimationStyle = s);
                },
              );
            }).toList(),
          ),
          const SizedBox(height: 16),

          // Box Height Slider
          Row(
            children: [
              Text(
                'Box Height (${_boxHeight.toInt()}px):',
                style: GoogleFonts.outfit(
                    fontSize: 13, fontWeight: FontWeight.w600, color: textSecondary),
              ),
              Expanded(
                child: Slider(
                  value: _boxHeight,
                  min: 50,
                  max: 84,
                  onChanged: (val) => setState(() => _boxHeight = val),
                ),
              ),
            ],
          ),

          // Border Radius Slider
          Row(
            children: [
              Text(
                'Border Radius (${_borderRadius.toInt()}px):',
                style: GoogleFonts.outfit(
                    fontSize: 13, fontWeight: FontWeight.w600, color: textSecondary),
              ),
              Expanded(
                child: Slider(
                  value: _borderRadius,
                  min: 4,
                  max: 32,
                  onChanged: (val) => setState(() => _borderRadius = val),
                ),
              ),
            ],
          ),
          const SizedBox(height: 12),

          // Obscure toggle & character selection
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                'Obscure Text (PIN Mode):',
                style: GoogleFonts.outfit(
                    fontSize: 13, fontWeight: FontWeight.w600, color: textSecondary),
              ),
              Switch(
                value: _obscureText,
                onChanged: (val) => setState(() => _obscureText = val),
              ),
            ],
          ),
          if (_obscureText) ...[
            const SizedBox(height: 8),
            Wrap(
              spacing: 8,
              children: ['●', '*', 'β˜…', 'β™₯'].map((char) {
                return ChoiceChip(
                  label: Text(char),
                  selected: _obscuringCharacter == char,
                  onSelected: (sel) {
                    if (sel) setState(() => _obscuringCharacter = char);
                  },
                );
              }).toList(),
            ),
          ],
        ],
      ),
    );
  }
  _StyleMeta _getStyleInfo(DemoStyle style) {
    return switch (style) {
      DemoStyle.nexus => _StyleMeta(
          name: 'Nexus Lattice Mesh',
          shortName: 'Nexus',
          tag: 'NEURAL',
          description: 'Neural lattice network with interconnected node physics and orbital electron arcs.',
          correctCode: '4545',
          icon: Icons.hub_outlined,
          accentColor: const Color(0xFFFF453A),
        ),
      DemoStyle.sigil => _StyleMeta(
          name: 'Sigil Arc Flow',
          shortName: 'Sigil',
          tag: 'COSMIC',
          description: 'Dark-glowing cosmic slots with interactive autofill toast and smooth arc animations.',
          correctCode: '7890',
          icon: Icons.auto_awesome,
          accentColor: const Color(0xFFA855F7),
        ),
      DemoStyle.liquid => _StyleMeta(
          name: 'Liquid Motion',
          shortName: 'Liquid',
          tag: 'AQUATIC',
          description: 'Glass containers filled with dynamic wavy liquid, floating bubbles & splash level transitions.',
          correctCode: '1234',
          icon: Icons.water_drop_outlined,
          accentColor: const Color(0xFF3B82F6),
        ),
      DemoStyle.lightning => _StyleMeta(
          name: 'Lightning Discharge',
          shortName: 'Lightning',
          tag: 'ELECTRIC',
          description: 'Electric discharge sparks running around outline paths with energy flare feedback.',
          correctCode: '4565',
          icon: Icons.bolt_rounded,
          accentColor: const Color(0xFFF59E0B),
        ),
      DemoStyle.motion => _StyleMeta(
          name: 'Motion Elastic',
          shortName: 'Motion',
          tag: 'PHYSICS',
          description: 'Fluid spring physics with elastic pop-in and celebration checkmark transitions.',
          correctCode: '1234',
          icon: Icons.motion_photos_on_rounded,
          accentColor: const Color(0xFF6366F1),
        ),
      DemoStyle.standard => _StyleMeta(
          name: 'Standard Premium',
          shortName: 'Standard',
          tag: 'CLASSIC',
          description: 'Highly customizable digit boxes with scaling entry animations, PIN masking & loaders.',
          correctCode: '123456',
          icon: Icons.pin_outlined,
          accentColor: const Color(0xFF4F6EF7),
        ),
    };
  }
}

class _StyleMeta {
  final String name;
  final String shortName;
  final String tag;
  final String description;
  final String correctCode;
  final IconData icon;
  final Color accentColor;

  _StyleMeta({
    required this.name,
    required this.shortName,
    required this.tag,
    required this.description,
    required this.correctCode,
    required this.icon,
    required this.accentColor,
  });
}
10
likes
160
points
305
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A highly customizable, beautiful, and interactive OTP and PIN entry widget for Flutter featuring Sigil, Nexus, Lightning, Liquid, and Motion animations.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, google_fonts

More

Packages that depend on premium_otp_input