otp_resend_timer 1.0.0 copy "otp_resend_timer: ^1.0.0" to clipboard
otp_resend_timer: ^1.0.0 copied to clipboard

Flutter widget that handle OTP resend functionality with customizable UI, animations, and callback support.

example/lib/main.dart

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'OTP Resend Timer Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late OtpResendTimerController _controller;
  final TextEditingController _otpController = TextEditingController();
  final List<FocusNode> _focusNodes = List.generate(4, (index) => FocusNode());
  final List<TextEditingController> _digitControllers = 
      List.generate(4, (index) => TextEditingController());
  
  @override
  void initState() {
    super.initState();
    _controller = OtpResendTimerController(initialTime: 10);
  }
  
  @override
  void dispose() {
    _controller.dispose();
    _otpController.dispose();
    for (var node in _focusNodes) {
      node.dispose();
    }
    for (var controller in _digitControllers) {
      controller.dispose();
    }
    super.dispose();
  }
  
  void _onResendClicked() {
    // Resend OTP logic here
  }

  void _onDigitInput(String value, int index) {
    if (value.length == 1) {
      if (index < 3) {
        _focusNodes[index + 1].requestFocus();
      } else {
        // Last digit entered, hide keyboard
        FocusManager.instance.primaryFocus?.unfocus();
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    
    return Scaffold(
      appBar: AppBar(
        backgroundColor: theme.colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const SizedBox(height: 30),
              // Example of a mock OTP input field
              Container(
                margin: const EdgeInsets.symmetric(horizontal: 24),
                padding: const EdgeInsets.all(24),
                decoration: BoxDecoration(
                  color: Colors.white,
                  border: Border.all(color: Colors.grey.shade300),
                  borderRadius: BorderRadius.circular(12),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.grey.shade200,
                      blurRadius: 10,
                      offset: const Offset(0, 5),
                    ),
                  ],
                ),
                child: Column(
                  children: [
                    const Text(
                      'Enter OTP Code',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 10),
                    const Text(
                      'We\'ve sent a verification code to your phone',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.grey,
                      ),
                    ),
                    const SizedBox(height: 30),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: List.generate(
                        4,
                        (index) => SizedBox(
                          width: 50,
                          height: 60,
                          child: TextField(
                            controller: _digitControllers[index],
                            focusNode: _focusNodes[index],
                            textAlign: TextAlign.center,
                            keyboardType: TextInputType.number,
                            maxLength: 1,
                            onChanged: (value) => _onDigitInput(value, index),
                            decoration: InputDecoration(
                              counterText: '',
                              contentPadding: EdgeInsets.zero,
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(8),
                                borderSide: BorderSide(color: theme.primaryColor),
                              ),
                              focusedBorder: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(8),
                                borderSide: BorderSide(color: theme.primaryColor, width: 2),
                              ),
                            ),
                            style: const TextStyle(
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(height: 30),
                    SizedBox(
                      width: double.infinity,
                      child: ElevatedButton(
                        onPressed: () {
                          // String otp = _digitControllers.map((c) => c.text).join();
                        },
                        style: ElevatedButton.styleFrom(
                          backgroundColor: theme.primaryColor,
                          foregroundColor: Colors.white,
                          padding: const EdgeInsets.symmetric(vertical: 12),
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                        child: const Text('Verify'),
                      ),
                    ),
                    const SizedBox(height: 20),
                    
                    // OtpResendTimer
                    Padding(
                      padding: const EdgeInsets.symmetric(vertical: 8.0),
                      child: OtpResendTimer(
                        controller: _controller,
                        onResendClicked: _onResendClicked,
                        autoStart: true,
                        timerMessage: 'Resend OTP in ',
                        readyMessage: 'You can now resend the code',
                        holdMessage: 'Start timer to enable resend',
                        resendMessage: 'Resend',
                        timerMessageStyle: TextStyle(
                          color: theme.primaryColor,
                          fontSize: 12,
                        ),
                        resendMessageStyle: TextStyle(
                          color: theme.primaryColor,
                          fontSize: 12,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
copied to clipboard
9
likes
150
points
106
downloads

Publisher

unverified uploader

Weekly Downloads

2024.09.26 - 2025.04.10

Flutter widget that handle OTP resend functionality with customizable UI, animations, and callback support.

Repository (GitHub)
View/report issues

Topics

#flutter #dart #otp #animation #resend

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on otp_resend_timer