import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import '../../Notifier/RentalContactProvider .dart'; import '../../Utility/AdvancedSnackbar.dart'; import '../../Utility/CustomSnackbar.dart'; class OtpScreen extends StatefulWidget { final String mob; // ✅ phone number final int otp; // ✅ backend OTP const OtpScreen({ super.key, required this.mob, required this.otp, }); @override State createState() => _OtpScreenState(); } class _OtpScreenState extends State { final List _controllers = List.generate(4, (_) => TextEditingController()); final List _focusNodes = List.generate(4, (_) => FocusNode()); bool _isValid = false; bool _isVerifying = false; bool _isResending = false; int _secondsRemaining = 22; int _currentOtp = 0; // ✅ Store current OTP in state @override void initState() { super.initState(); _currentOtp = widget.otp; // ✅ Initialize with passed OTP _startTimer(); } @override void dispose() { for (var controller in _controllers) { controller.dispose(); } for (var focusNode in _focusNodes) { focusNode.dispose(); } super.dispose(); } void _startTimer() { Future.doWhile(() async { if (_secondsRemaining > 0) { await Future.delayed(const Duration(seconds: 1)); if (mounted) { setState(() => _secondsRemaining--); } return true; } return false; }); } void _onOtpChange() { final otp = _controllers.map((c) => c.text).join(); setState(() { _isValid = otp.length == 4 && RegExp(r'^[0-9]{4}$').hasMatch(otp); }); } Future _verifyOtp() async { final enteredOtp = _controllers.map((c) => c.text).join(); setState(() => _isVerifying = true); await Future.delayed(const Duration(milliseconds: 800)); // ✅ Compare with current OTP (not widget.otp) if (enteredOtp == _currentOtp.toString()) { // ✅ OTP verified successfully if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("✅ OTP Verified Successfully!"), backgroundColor: Colors.green, ), ); // Example navigation: // Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const HomeScreen())); } else { // ❌ Invalid OTP ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("❌ Invalid OTP. Please try again."), backgroundColor: Colors.redAccent, ), ); } if (mounted) setState(() => _isVerifying = false); } /// 🔁 Resend OTP Function Future _resendOtp(BuildContext context) async { setState(() => _isResending = true); try { final rentalProvider = Provider.of(context, listen: false); await rentalProvider.fetchRentalContactData( context, "4d21382d9e1c4d6e0b7c426d53d89b6b7d48078877f185289092e6fa13bac4b11d417d37738b20b34151b8e638625b3ec013", "5", widget.mob, ); if (rentalProvider.rentalContact != null && rentalProvider.rentalContact!.error == 0) { // ✅ Update current OTP with the new one from API response final newOtp = rentalProvider.rentalContact!.otp!; setState(() { _currentOtp = newOtp; // ✅ Update current OTP _secondsRemaining = 22; }); // ✅ Clear previous OTP fields for (var controller in _controllers) { controller.clear(); } setState(() { _isValid = false; }); // ✅ Restart timer _startTimer(); AnimatedSnackBar.success( context: context, title: "OTP Sent", message: "${rentalProvider.rentalContact?.message} (OTP: $newOtp)", ); } else { CustomSnackBar.showWarning( context: context, title: "Error", message: rentalProvider.rentalContact?.message ?? rentalProvider.errorMessage ?? "Failed to resend OTP", ); } } catch (e) { CustomSnackBar.showError( context: context, title: "Network Error", message: e.toString(), ); } finally { if (mounted) setState(() => _isResending = false); } } Widget _buildOtpField(int index) { return SizedBox( width: 60, height: 60, child: TextField( controller: _controllers[index], focusNode: _focusNodes[index], keyboardType: TextInputType.number, textAlign: TextAlign.center, maxLength: 1, style: const TextStyle( fontSize: 24, color: Colors.black, fontWeight: FontWeight.w600, ), decoration: InputDecoration( counterText: "", filled: true, fillColor: Colors.white, enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(50), borderSide: BorderSide(color: Colors.grey.withOpacity(0.5), width: 1), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(50), borderSide: const BorderSide(color: Color(0xFF008CDE), width: 2), ), ), onChanged: (value) { _onOtpChange(); if (value.isNotEmpty && index < 3) { FocusScope.of(context).requestFocus(_focusNodes[index + 1]); } else if (value.isEmpty && index > 0) { FocusScope.of(context).requestFocus(_focusNodes[index - 1]); } }, ), ); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, body: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: Stack( children: [ // 🏙️ Background image Positioned.fill( child: Image.asset( 'assets/images/background.jpg', fit: BoxFit.cover, ), ), // 🌑 Overlay Positioned.fill( child: Container(color: Colors.black.withOpacity(0.5)), ), // 🌈 Gradient overlay Positioned.fill( child: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black54, Colors.black87], ), ), ), ), // 📦 Foreground content SingleChildScrollView( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom + 20, ), child: ConstrainedBox( constraints: BoxConstraints(minHeight: MediaQuery.of(context).size.height), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ const SizedBox(height: 40), const Padding( padding: EdgeInsets.symmetric(horizontal: 24), child: Text( "Rental Power,\nManaged in a Tap", textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 32, fontWeight: FontWeight.w500, height: 1.3, ), ), ), const SizedBox(height: 40), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text( "Enter the OTP sent to +91 ${widget.mob}", textAlign: TextAlign.center, style: const TextStyle( color: Colors.white70, fontSize: 14, ), ), ), const SizedBox(height: 30), Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(4, _buildOtpField), ), ), const SizedBox(height: 20), // 🔁 Resend OTP TextButton( onPressed: _secondsRemaining == 0 && !_isResending ? () => _resendOtp(context) : null, child: _isResending ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : Text( _secondsRemaining == 0 ? "Resend OTP" : "Resend OTP in $_secondsRemaining s", style: TextStyle( color: _secondsRemaining == 0 ? const Color(0xFF008CDE) : Colors.white70, fontSize: 14, decoration: _secondsRemaining == 0 ? TextDecoration.underline : TextDecoration.none, ), ), ), const SizedBox(height: 30), // 🔘 Continue Button Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isValid && !_isVerifying ? _verifyOtp : null, style: ElevatedButton.styleFrom( backgroundColor: _isValid ? const Color(0xFF008CDE) : const Color(0xFF266E99), foregroundColor: Colors.white, disabledBackgroundColor: const Color(0xFF266E99), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), padding: const EdgeInsets.symmetric(vertical: 16), ), child: _isVerifying ? const SizedBox( width: 22, height: 22, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : Padding( padding: const EdgeInsets.symmetric(horizontal: 22), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Continue", style: TextStyle( color: _isValid ? Colors.white : const Color(0xFF03456C), fontSize: 16, ), ), SvgPicture.asset( "assets/svg/continue_ic.svg", color: _isValid ? Colors.white : const Color(0xFF03456C), height: 25, width: 25, ), ], ), ), ), ), ), const SizedBox(height: 40), ], ), ), ), ], ), ), ); } }