import 'package:wanisclient/utils/colors.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../config/AppSnackbar.dart'; import '../config/custom_text.dart'; import '../config/language/language_enum.dart'; import '../providers/language_provider.dart'; import '../widgets/custom_button.dart'; class PaymentMethodScreen extends StatefulWidget { @override _PaymentMethodScreenState createState() => _PaymentMethodScreenState(); } class _PaymentMethodScreenState extends State { final _formKey = GlobalKey(); final _cardNumberController = TextEditingController(); final _cardHolderController = TextEditingController(); final _expirationController = TextEditingController(); final _cvvController = TextEditingController(); // Focus nodes to track which field is being edited final _cardNumberFocus = FocusNode(); final _cardHolderFocus = FocusNode(); final _expirationFocus = FocusNode(); final _cvvFocus = FocusNode(); @override void dispose() { _cardNumberController.dispose(); _cardHolderController.dispose(); _expirationController.dispose(); _cvvController.dispose(); _cardNumberFocus.dispose(); _cardHolderFocus.dispose(); _expirationFocus.dispose(); _cvvFocus.dispose(); super.dispose(); } String _formatCardNumber(String value) { value = value.replaceAll(' ', ''); String formatted = ''; for (int i = 0; i < value.length; i++) { if (i > 0 && i % 4 == 0) { formatted += ' '; } formatted += value[i]; } return formatted; } String _formatExpiration(String value) { value = value.replaceAll('/', ''); if (value.length >= 2) { return value.substring(0, 2) + '/' + value.substring(2); } return value; } // Validation functions bool _isValidCardNumber(String cardNumber) { String cleanNumber = cardNumber.replaceAll(' ', ''); return cleanNumber.length >= 13 && cleanNumber.length <= 19; } bool _isValidCardHolder(String name) { return name.trim().length >= 2 && RegExp(r'^[a-zA-Z\s]+$').hasMatch(name.trim()); } bool _isValidExpiration(String expiration) { if (!expiration.contains('/') || expiration.length != 5) return false; List parts = expiration.split('/'); if (parts.length != 2) return false; int? month = int.tryParse(parts[0]); int? year = int.tryParse(parts[1]); if (month == null || year == null) return false; if (month < 1 || month > 12) return false; // Check if the card is not expired DateTime now = DateTime.now(); int fullYear = 2000 + year; DateTime cardDate = DateTime(fullYear, month + 1, 0); // Last day of the month return cardDate.isAfter(now); } bool _isValidCVV(String cvv) { return cvv.length >= 3 && cvv.length <= 4; } // Validation with snackbar functions void _validateCardNumber() { if (_cardNumberController.text.isNotEmpty && !_isValidCardNumber(_cardNumberController.text)) { AppSnackbar.showError("Error", 'Please enter a valid card number (13-19 digits)' ); } } void _validateCardHolder() { if (_cardHolderController.text.isNotEmpty && !_isValidCardHolder(_cardHolderController.text)) { AppSnackbar.showError("Error", 'Please enter a valid name (letters and spaces only)' ); } } void _validateExpiration() { if (_expirationController.text.isNotEmpty && !_isValidExpiration(_expirationController.text)) { if (_expirationController.text.length == 5) { AppSnackbar.showError("Error", 'Please enter a valid future date (MM/YY)' ); } } } void _validateCVV() { if (_cvvController.text.isNotEmpty && !_isValidCVV(_cvvController.text)) { AppSnackbar.showError("Error", 'Please enter a valid CVV (3-4 digits)' ); } } void _validateAllFields() { List errors = []; // Collect all validation errors first if (_cardNumberController.text.isEmpty) { errors.add('Card number is required'); } else if (!_isValidCardNumber(_cardNumberController.text)) { errors.add('Card number must be 13-19 digits'); } if (_cardHolderController.text.isEmpty) { errors.add('Card holder name is required'); } else if (!_isValidCardHolder(_cardHolderController.text)) { errors.add('Card holder name must contain only letters and spaces'); } if (_expirationController.text.isEmpty) { errors.add('Expiration date is required'); } else if (!_isValidExpiration(_expirationController.text)) { errors.add('Expiration date must be valid and in the future'); } if (_cvvController.text.isEmpty) { errors.add('CVV is required'); } else if (!_isValidCVV(_cvvController.text)) { errors.add('CVV must be 3-4 digits'); } // Show only one snackbar with appropriate message if (errors.isNotEmpty) { if (errors.length == 1) { AppSnackbar.showError("Error", errors.first); } else { AppSnackbar.showError("Error", 'multiple fields need correction'); } } else { // All fields are valid AppSnackbar.showSuccess("Success", 'Your payment method has been successfully added!'); // Process the payment method here print('Card Number: ${_cardNumberController.text}'); print('Card Holder: ${_cardHolderController.text}'); print('Expiration: ${_expirationController.text}'); print('CVV: ${_cvvController.text}'); } } @override Widget build(BuildContext context) { final languageProvider = context.watch(); bool isArabic = languageProvider.currentLangCode == Language.ar; return Scaffold( backgroundColor: AppNewColors.white, body: Padding( padding: const EdgeInsets.all(24.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( onTap: () { Navigator.pop(context); }, child: Container( width: 32, height: 32, margin: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: AppNewColors.lightgray, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: AppNewColors.black.withOpacity(.1), blurRadius: 1, offset: const Offset(2, 1), ), ], ), child: const Icon( Icons.arrow_back, color: AppNewColors.black, size: 16, ), ), ), Expanded( child: Center( child: Txt( text: 'Payment Method', style: TextStyle( color: AppNewColors.titleTextColor, fontSize: 18, fontFamily: isArabic ? "Readex_Pro" : 'Inter', fontWeight: FontWeight.w600, height: 1.23, ), ), ), ), const SizedBox(width: 32), ], ), ), Txt( text: 'New Payment Method', style: TextStyle( color: Colors.black, fontSize: 26, height: 1.23, fontWeight: FontWeight.w600, fontFamily: isArabic ? "Readex_Pro" : 'Inter', ), ), const SizedBox(height: 8), Txt( text: 'Enter your new payment method\ninformation.', style: TextStyle( color: const Color(0xFF6F6F6B), fontSize: 18, height: 1.33, fontFamily: isArabic ? "Readex_Pro" : 'Inter', ), ), const SizedBox(height: 32), // Card Number Field Txt( text: 'Card Number', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, height: 1.85, color: const Color(0xFF8C929C), fontFamily: isArabic ? "Readex_Pro" : 'Inter', ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( color: AppNewColors.inputColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: TextFormField( controller: _cardNumberController, focusNode: _cardNumberFocus, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(16), ], onChanged: (value) { String formatted = _formatCardNumber(value); if (formatted != _cardNumberController.text) { _cardNumberController.value = TextEditingValue( text: formatted, selection: TextSelection.collapsed(offset: formatted.length), ); } }, onEditingComplete: () { _validateCardNumber(); _cardHolderFocus.requestFocus(); }, decoration: const InputDecoration( hintText: '1234 5678 8923 1345', hintStyle: TextStyle(color: AppNewColors.inputTextColor), prefixIcon: Icon(Icons.credit_card, color: AppNewColors.inputTextColor), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), ), ), ), const SizedBox(height: 24), // Card Holder Name Field Txt( text: 'Card Holder Name', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, height: 1.85, color: const Color(0xFF8C929C), fontFamily: isArabic ? "Readex_Pro" : 'Inter', ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( color: AppNewColors.inputColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: TextFormField( controller: _cardHolderController, focusNode: _cardHolderFocus, textCapitalization: TextCapitalization.words, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\s]')), ], onEditingComplete: () { _validateCardHolder(); _expirationFocus.requestFocus(); }, decoration: const InputDecoration( hintText: 'Flen Ben Foulen', hintStyle: TextStyle(color: AppNewColors.inputTextColor), prefixIcon: Icon(Icons.person_outline, color: AppNewColors.inputTextColor), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), ), ), ), const SizedBox(height: 24), // Expiration and CVV Row Row( children: [ Expanded( flex: 2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Txt( text: 'Expiration', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, height: 1.85, color: const Color(0xFF8C929C), fontFamily: isArabic ? "Readex_Pro" : 'Inter', ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( color: AppNewColors.inputColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: TextFormField( controller: _expirationController, focusNode: _expirationFocus, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(4), ], onChanged: (value) { String formatted = _formatExpiration(value); if (formatted != _expirationController.text) { _expirationController.value = TextEditingValue( text: formatted, selection: TextSelection.collapsed(offset: formatted.length), ); } }, onEditingComplete: () { _validateExpiration(); _cvvFocus.requestFocus(); }, decoration: const InputDecoration( hintText: 'MM/YY', hintStyle: TextStyle(color: AppNewColors.inputTextColor), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), ), ), ), ], ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Txt( text: 'CVV', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, height: 1.85, color: const Color(0xFF8C929C), fontFamily: isArabic ? "Readex_Pro" : 'Inter', ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( color: AppNewColors.inputColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: TextFormField( controller: _cvvController, focusNode: _cvvFocus, keyboardType: TextInputType.number, obscureText: true, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(4), ], onEditingComplete: () { _validateCVV(); }, decoration: const InputDecoration( hintText: '•••', hintStyle: TextStyle(color: AppNewColors.inputTextColor), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 16), ), ), ), ], ), ), ], ), const Spacer(), CustomButton( onClick: _validateAllFields, width: double.infinity, height: 56, fontSize: 18, borderRadius: 10, title: "Continue", backgroundColor: AppNewColors.primaryBtnColor, borderColor: AppNewColors.primaryBtnColor, txtColor: AppNewColors.white, loadercolor: AppNewColors.white, ), const SizedBox(height: 24), ], ), ), ), ); } }