import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:kmobile/api/services/beneficiary_service.dart'; import 'package:kmobile/data/models/ifsc.dart'; import 'package:kmobile/data/models/beneficiary.dart'; import 'beneficiary_result_page.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../../../di/injection.dart'; import '../../../l10n/app_localizations.dart'; class AddBeneficiaryScreen extends StatefulWidget { const AddBeneficiaryScreen({super.key}); @override State createState() => _AddBeneficiaryScreen(); } class _AddBeneficiaryScreen extends State { final _formKey = GlobalKey(); final TextEditingController accountNumberController = TextEditingController(); final TextEditingController confirmAccountNumberController = TextEditingController(); final TextEditingController nameController = TextEditingController(); final TextEditingController bankNameController = TextEditingController(); final TextEditingController branchNameController = TextEditingController(); final TextEditingController ifscController = TextEditingController(); final TextEditingController phoneController = TextEditingController(); String? _beneficiaryName; bool _isValidating = false; bool _isBeneficiaryValidated = false; String? _validationError; late String accountType; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { accountType = AppLocalizations.of(context).savings; }); }); } ifsc? _ifscData; bool _isLoading = false; //for validateIFSC() void _validateIFSC() async { var beneficiaryService = getIt(); final ifsc = ifscController.text.trim().toUpperCase(); if (ifsc.isEmpty) return; setState(() { _isLoading = true; _ifscData = null; }); final result = await beneficiaryService.validateIFSC(ifsc); setState(() { _isLoading = false; _ifscData = result; }); if (result == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(AppLocalizations.of(context).invalidIfsc)), ); bankNameController.clear(); branchNameController.clear(); } else { print("${AppLocalizations.of(context).validIfsc}: ${result.bankName}, ${result.branchName}"); bankNameController.text = result.bankName; branchNameController.text = result.branchName; } } Future _validateBeneficiary() async { var beneficiaryService = getIt(); setState(() { _isValidating = true; _validationError = null; }); try { final name = await beneficiaryService .validateBeneficiaryWithinBank(accountNumberController.text); setState(() { _beneficiaryName = name; _isBeneficiaryValidated = true; _isValidating = false; }); } catch (e) { setState(() { _validationError = "Account Number not from KCCB"; _isValidating = false; _isBeneficiaryValidated = false; _beneficiaryName = null; }); } } String _selectedAccountType = 'Savings'; // default value void validateAndAddBeneficiary() async { // Show spinner and disable UI showDialog( context: context, barrierDismissible: false, // Prevent dismiss on tap outside builder: (BuildContext context) { return WillPopScope( onWillPop: () async => false, // Disable back button child: const Center( child: CircularProgressIndicator(), ), ); }, ); final beneficiary = Beneficiary( accountNo: accountNumberController.text.trim(), accountType: _selectedAccountType, name: nameController.text.trim(), ifscCode: ifscController.text.trim(), ); var service = getIt(); try { await service.sendForValidation(beneficiary); bool isFound = await service.checkIfFound(beneficiary.accountNo); if (context.mounted) { Navigator.pop(context); // Close the spinner Navigator.push( context, MaterialPageRoute( builder: (context) => BeneficiaryResultPage(isSuccess: isFound), ), ); } } catch (e) { Navigator.pop(context); // Close the spinner ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(AppLocalizations.of(context).somethingWentWrong)), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Symbols.arrow_back_ios_new), onPressed: () { Navigator.pop(context); }, ), title: Text( AppLocalizations.of(context).addBeneficiary, style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), ), centerTitle: false, actions: [ Padding( padding: const EdgeInsets.only(right: 10.0), child: CircleAvatar( backgroundColor: Colors.grey[200], radius: 20, child: SvgPicture.asset( 'assets/images/avatar_male.svg', width: 40, height: 40, fit: BoxFit.cover, ), ), ), ], ), body: SafeArea( child: Form( key: _formKey, child: Column( children: [ Expanded( child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: Padding( padding: const EdgeInsets.all(10.0), child: Column( children: [ TextFormField( controller: accountNumberController, decoration: InputDecoration( labelText: AppLocalizations.of( context, ).accountNumber, // prefixIcon: Icon(Icons.person), border: OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), obscureText: true, keyboardType: TextInputType.number, textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.length < 10) { return AppLocalizations.of( context, ).enterValidAccountNumber; } return null; }, ), const SizedBox(height: 24), // Confirm Account Number TextFormField( controller: confirmAccountNumberController, decoration: InputDecoration( labelText: AppLocalizations.of( context, ).confirmAccountNumber, // prefixIcon: Icon(Icons.person), border: OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), keyboardType: TextInputType.number, textInputAction: TextInputAction.next, validator: (value) { if (value == null || value.isEmpty) { return AppLocalizations.of( context, ).reenterAccountNumber; } if (value != accountNumberController.text) { return AppLocalizations.of( context, ).accountMismatch; } return null; }, ), const SizedBox(height: 24), // 🔹 IFSC Code Field TextFormField( controller: ifscController, decoration: InputDecoration( labelText: AppLocalizations.of(context).ifscCode, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, onFieldSubmitted: (_) { _validateIFSC(); }, onChanged: (value) { final trimmed = value.trim().toUpperCase(); if (trimmed.length < 11) { // clear bank/branch if backspace or changed bankNameController.clear(); branchNameController.clear(); } }, validator: (value) { final pattern = RegExp(r'^[A-Z]{4}0[A-Z0-9]{6}$'); if (value == null || value.trim().isEmpty) { return AppLocalizations.of(context).enterIfsc; } else if (!pattern.hasMatch( value.trim().toUpperCase(), )) { return AppLocalizations.of( context, ).invalidIfscFormat; } return null; }, ), const SizedBox(height: 24), // 🔹 Bank Name (Disabled) TextFormField( controller: bankNameController, enabled: false, // changed from readOnly to disabled decoration: InputDecoration( labelText: AppLocalizations.of(context).bankName, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).dialogBackgroundColor, // disabled color enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), ), const SizedBox(height: 24), // 🔹 Branch Name (Disabled) TextFormField( controller: branchNameController, enabled: false, // changed from readOnly to disabled decoration: InputDecoration( labelText: AppLocalizations.of(context).branchName, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).dialogBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), ), //validate Beneficiary Button if (!_isBeneficiaryValidated) Padding( padding: const EdgeInsets.only(top: 12.0), child: SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isValidating ? null : () { if (accountNumberController.text.length == 11 && confirmAccountNumberController.text == accountNumberController.text) { _validateBeneficiary(); } else { setState(() { _validationError = 'Please enter a valid and matching account number.'; }); } }, child: _isValidating ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Validate Beneficiary'), ), ), ), //Beneficiary Name (Disabled) const SizedBox(height: 24), TextFormField( controller: nameController, enabled: false, decoration: InputDecoration( labelText: AppLocalizations.of(context).beneficiaryName, // prefixIcon: Icon(Icons.person), border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).dialogBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), textInputAction: TextInputAction.next, validator: (value) => value == null || value.isEmpty ? AppLocalizations.of(context).nameRequired : null, ), const SizedBox(height: 24), // 🔹 Account Type Dropdown DropdownButtonFormField( value: accountType, decoration: InputDecoration( labelText: AppLocalizations.of(context).accountType, border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), items: [ AppLocalizations.of(context).savings, AppLocalizations.of(context).current, ] .map( (type) => DropdownMenuItem( value: type, child: Text(type), ), ) .toList(), onChanged: (value) { setState(() { accountType = value!; }); }, ), const SizedBox(height: 24), TextFormField( controller: phoneController, keyboardType: TextInputType.phone, decoration: InputDecoration( labelText: AppLocalizations.of(context).phone, prefixIcon: const Icon(Icons.phone), border: const OutlineInputBorder(), isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.black), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.black, width: 2, ), ), ), textInputAction: TextInputAction.done, validator: (value) => value == null || value.length != 10 ? AppLocalizations.of(context).enterValidPhone : null, ), const SizedBox(height: 35), ], ), ), ), ), Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( width: 250, child: ElevatedButton( onPressed: validateAndAddBeneficiary, style: ElevatedButton.styleFrom( shape: const StadiumBorder(), padding: const EdgeInsets.symmetric(vertical: 16), backgroundColor: Theme.of(context).primaryColorDark, foregroundColor: Theme.of(context).scaffoldBackgroundColor, ), child: Text(AppLocalizations.of(context).validateAndAdd), ), ), ), ], ), ), ), ); } }