import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; class OTPVerificationPage extends StatefulWidget { final String phoneNumber; const OTPVerificationPage({Key? key, required this.phoneNumber}) : super(key: key); @override State createState() => _OTPVerificationPageState(); } class _OTPVerificationPageState extends State { final int _otpLength = 4; late final List _controllers; late final List _focusNodes; Timer? _resendTimer; int _resendTime = 60; bool _isOtpComplete = false; @override void initState() { super.initState(); _controllers = List.generate(_otpLength, (_) => TextEditingController()); _focusNodes = List.generate(_otpLength, (_) => FocusNode()); _startResendTimer(); // Focus the first field once the screen is built WidgetsBinding.instance.addPostFrameCallback((_) { if (_focusNodes.isNotEmpty) { FocusScope.of(context).requestFocus(_focusNodes[0]); } }); } @override void dispose() { for (final c in _controllers) c.dispose(); for (final f in _focusNodes) f.dispose(); _resendTimer?.cancel(); super.dispose(); } void _startResendTimer() { _resendTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (_resendTime > 0) { setState(() => _resendTime--); } else { timer.cancel(); } }); } void _onOtpChanged(int index, String value) { if (value.length == 1 && index < _otpLength - 1) { _focusNodes[index + 1].requestFocus(); } else if (value.isEmpty && index > 0) { _focusNodes[index - 1].requestFocus(); } _checkOtpCompletion(); } void _checkOtpCompletion() { final isComplete = _controllers.every((c) => c.text.isNotEmpty); if (isComplete != _isOtpComplete) { setState(() => _isOtpComplete = isComplete); if (isComplete) { _verifyOtp(); } } } void _resendOtp() { if (_resendTime == 0) { setState(() => _resendTime = 60); _startResendTimer(); autoFillOtp("1234"); // call resend API here } } String _getMaskedPhoneNumber() { final phone = widget.phoneNumber; return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone; } @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( hideLogoAndLang: true, onBackPressed: () { Navigator.of(context).pop(); }, onLanguageChanged: (lang) {}, ), body: SingleChildScrollView( child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 40.h), Text( 'OTP Verification', style: TextStyle(fontSize: 24.fSize, fontWeight: FontWeight.bold), ), SizedBox(height: 16.h), Text( 'We have sent you the OTP code on ${_getMaskedPhoneNumber()} via SMS for registration verification', style: TextStyle(fontSize: 16.fSize, color: Colors.grey), ), SizedBox(height: 40.h), // OTP Input Fields SizedBox( height: 100, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(_otpLength, (index) { return ValueListenableBuilder( valueListenable: _controllers[index], builder: (context, value, _) { final hasText = value.text.isNotEmpty; return AnimatedContainer( duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: 70.h, margin: EdgeInsets.symmetric(horizontal: 4.h), decoration: RoundedRectangleBorder() .toSmoothCornerDecoration(color: _isOtpComplete ? AppColors.successColor : (hasText ? AppColors.blackBgColor : AppColors.whiteColor), borderRadius: 16), child: Center( child: TextField( controller: _controllers[index], focusNode: _focusNodes[index], textAlign: TextAlign.center, keyboardType: TextInputType.number, maxLength: 1, style: TextStyle( fontSize: 40.fSize, fontWeight: FontWeight.bold, color: AppColors.whiteColor, ), decoration: InputDecoration( counterText: '', filled: true, fillColor: Colors.transparent, border: OutlineInputBorder( borderRadius: BorderRadius.circular(18), borderSide: BorderSide.none, ), ), onChanged: (v) => _onOtpChanged(index, v), ), ), ); }, ); }), ), ), const SizedBox(height: 32), // Resend OTP Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("Didn't receive it? "), if (_resendTime > 0) Text( 'resend in (${_resendTime.toString().padLeft(2, '0')}:00). ', style: const TextStyle(color: Colors.grey), ) else GestureDetector( onTap: _resendOtp, child: const Text( 'Resend', style: TextStyle( color: AppColors.primaryRedColor, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), ), ), ); } void _verifyOtp() { final otp = _controllers.map((c) => c.text).join(); debugPrint('Verifying OTP: $otp'); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Verifying OTP: $otp')), ); Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => RegisterNewStep2(null, {"nationalID": "12345678654321"}))); } /// Auto fill OTP into text fields void autoFillOtp(String otp) { if (otp.length != _otpLength) return; for (int i = 0; i < _otpLength; i++) { _controllers[i].text = otp[i]; } // Move focus to the last field _focusNodes[_otpLength - 1].requestFocus(); // Trigger completion check and color update _checkOtpCompletion(); } }