Edit Limit for beneficiaries and APY Register UI

This commit is contained in:
2026-03-13 18:17:03 +05:30
parent 7d39314d39
commit 075cb3aaad
6 changed files with 718 additions and 70 deletions

View File

@@ -0,0 +1,396 @@
import 'package:flutter/material.dart';
import 'package:kmobile/l10n/app_localizations.dart';
class APYRegisterScreen extends StatefulWidget {
final Map<String, dynamic>? initialData;
const APYRegisterScreen({super.key, this.initialData});
@override
State<APYRegisterScreen> createState() => _APYRegisterScreenState();
}
class _APYRegisterScreenState extends State<APYRegisterScreen> {
final _formKey = GlobalKey<FormState>();
// Controllers
late final _titleController = TextEditingController(text: widget.initialData?['customertitle']?.toString());
late final _firstNameController = TextEditingController(text: widget.initialData?['customerfirstname']?.toString());
late final _middleNameController = TextEditingController(text: widget.initialData?['customermiddlename']?.toString());
late final _lastNameController = TextEditingController(text: widget.initialData?['customerlastname']?.toString());
late final _customerNoController = TextEditingController(text: widget.initialData?['customerno']?.toString());
late final _accountNoController = TextEditingController(text: widget.initialData?['accountno']?.toString());
late final _balanceController = TextEditingController(text: widget.initialData?['availablebalance']?.toString());
late final _dobController = TextEditingController(text: widget.initialData?['customerdob']?.toString());
late final _genderController = TextEditingController(text: widget.initialData?['gender']?.toString());
late final _marriedController = TextEditingController(text: widget.initialData?['married']?.toString());
late final _mobileController = TextEditingController(text: widget.initialData?['mobilenumber']?.toString());
late final _emailController = TextEditingController(text: widget.initialData?['emailid']?.toString());
late final _aadhaarController = TextEditingController(text: widget.initialData?['aadharno']?.toString());
late final _pincodeController = TextEditingController(text: widget.initialData?['pincode']?.toString());
late final _pensionAmountController = TextEditingController(text: widget.initialData?['pensionamtoptedfor']?.toString() ?? '1000');
late final _ageOfJoiningController = TextEditingController(text: widget.initialData?['ageofjoining']?.toString());
late final _spouseNameController = TextEditingController(text: widget.initialData?['nameofspouse']?.toString());
late final _incomeTaxPayerController = TextEditingController(text: widget.initialData?['whetherincometaxpayer']?.toString());
late final _otherSchemeController = TextEditingController(text: widget.initialData?['beneficaryofothersociatysecurityschemes']?.toString());
late final _collectionChannelController = TextEditingController(text: widget.initialData?['collectionchannel']?.toString() ?? '1');
late final _contributionTypeController = TextEditingController(text: widget.initialData?['contributionType']?.toString() ?? 'C');
late final _debitDateController = TextEditingController(text: widget.initialData?['subsequentContributionDebitDate']?.toString());
late final _nomineeNameController = TextEditingController(text: widget.initialData?['nomineename']?.toString());
late final _nomineeRelationController = TextEditingController(text: widget.initialData?['relationwithsubscriber']?.toString());
late final _nomineeMinorController = TextEditingController(text: widget.initialData?['nomineeminor']?.toString() ?? 'N');
late final _nomineeDobController = TextEditingController();
late final _guardianNameController = TextEditingController();
late final _fatcaController = TextEditingController(text: widget.initialData?['fatcacrsapplicable']?.toString());
late final _birthCountryController = TextEditingController(text: widget.initialData?['countryofbirth']?.toString());
late final _citizenshipCountryController = TextEditingController(text: widget.initialData?['countryofcitizenship']?.toString());
late final _taxResidenceCountryController = TextEditingController(text: widget.initialData?['countryofresidencefortaxpurpose']?.toString());
late final _usPersonController = TextEditingController(text: widget.initialData?['uspersonflag']?.toString());
final Map<String, String> _yesNoOptions = {
'Y': 'Yes',
'N': 'No',
};
final Map<String, String> _titleOptions = {
'01': 'Mr.',
'02': 'Mrs.',
'03': 'Miss',
};
final Map<String, String> _pensionOptions = {
'1000': '₹1000',
'2000': '₹2000',
'3000': '₹3000',
'4000': '₹4000',
'5000': '₹5000',
};
final Map<String, String> _collectionChannelOptions = {
'1': 'Bank',
};
Map<String, String> _getPeriodicityOptions(AppLocalizations l10n) => {
'C': l10n.monthly,
'Q': l10n.quarterly,
'H': l10n.halfYearly,
};
// Contribution Calculation Map
final Map<String, Map<String, String>> _contributionRates = {
'C': {
'1000': '90',
'2000': '178',
'3000': '268',
'4000': '356',
'5000': '446',
},
'Q': {
'1000': '268',
'2000': '530',
'3000': '1061',
'4000': '356', // Following prompt's example exactly
'5000': '1329',
},
'H': {
'1000': '531',
'2000': '1050',
'3000': '1582',
'4000': '2101',
'5000': '2632',
},
};
String get _calculatedContribution {
final periodicity = _contributionTypeController.text;
final amount = _pensionAmountController.text;
return _contributionRates[periodicity]?[amount] ?? '0';
}
@override
void dispose() {
_titleController.dispose();
_firstNameController.dispose();
_middleNameController.dispose();
_lastNameController.dispose();
_customerNoController.dispose();
_accountNoController.dispose();
_balanceController.dispose();
_dobController.dispose();
_genderController.dispose();
_marriedController.dispose();
_mobileController.dispose();
_emailController.dispose();
_aadhaarController.dispose();
_pincodeController.dispose();
_pensionAmountController.dispose();
_ageOfJoiningController.dispose();
_spouseNameController.dispose();
_incomeTaxPayerController.dispose();
_otherSchemeController.dispose();
_collectionChannelController.dispose();
_contributionTypeController.dispose();
_debitDateController.dispose();
_nomineeNameController.dispose();
_nomineeRelationController.dispose();
_nomineeMinorController.dispose();
_nomineeDobController.dispose();
_guardianNameController.dispose();
_fatcaController.dispose();
_birthCountryController.dispose();
_citizenshipCountryController.dispose();
_taxResidenceCountryController.dispose();
_usPersonController.dispose();
super.dispose();
}
bool _isFetched(String key) {
return widget.initialData != null &&
widget.initialData!.containsKey(key) &&
widget.initialData![key]?.toString().isNotEmpty == true;
}
void _handleRegister() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Registration logic to be implemented')),
);
}
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: Text(l10n.apyRegistration),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Tile 1: Customer & APY Details
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.details,
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
_buildDropdownField(_titleController, l10n.customerTitle,
_titleOptions,
readOnly: _isFetched('customertitle')),
_buildTextField(
_firstNameController, l10n.customerFirstName,
readOnly: _isFetched('customerfirstname')),
_buildTextField(
_middleNameController, l10n.customerMiddleName,
readOnly: _isFetched('customermiddlename')),
_buildTextField(
_lastNameController, l10n.customerLastName,
readOnly: _isFetched('customerlastname')),
_buildTextField(_accountNoController, l10n.accountNumber,
keyboardType: TextInputType.number,
readOnly: _isFetched('accountno')),
_buildTextField(
_balanceController, l10n.availableBalance,
keyboardType: TextInputType.number,
readOnly: _isFetched('availablebalance')),
_buildTextField(_dobController, l10n.customerDobFormat,
mandatory: true, readOnly: _isFetched('customerdob')),
_buildTextField(_emailController, l10n.emailId,
keyboardType: TextInputType.emailAddress,
readOnly: _isFetched('emailid')),
_buildDropdownField(
_marriedController, l10n.marriedYesNo, _yesNoOptions,
mandatory: true),
_buildTextField(_nomineeNameController, l10n.nomineeName,
mandatory: true),
_buildDropdownField(_nomineeMinorController,
l10n.nomineeMinor, _yesNoOptions,
mandatory: true, onChanged: (val) {
setState(() {
_nomineeMinorController.text = val ?? 'N';
});
}),
if (_nomineeMinorController.text == 'Y') ...[
_buildTextField(_nomineeDobController, l10n.nomineeDob,
mandatory: true),
_buildTextField(
_guardianNameController, l10n.guardianName,
mandatory: true),
],
_buildDropdownField(_otherSchemeController,
l10n.isBeneficiaryOtherScheme, _yesNoOptions,
mandatory: true),
_buildDropdownField(_incomeTaxPayerController,
l10n.isIncomeTaxPayer, _yesNoOptions,
mandatory: true),
_buildTextField(_spouseNameController, l10n.nameOfSpouse,
mandatory: true),
_buildTextField(_ageOfJoiningController, l10n.ageOfJoining,
keyboardType: TextInputType.number, mandatory: true),
_buildDropdownField(_collectionChannelController,
l10n.collectionChannel, _collectionChannelOptions,
mandatory: true),
_buildDropdownField(
_fatcaController, l10n.fatcaApplicable, _yesNoOptions),
_buildTextField(
_birthCountryController, l10n.countryOfBirth),
_buildTextField(
_citizenshipCountryController, l10n.countryOfCitizenship),
_buildTextField(_taxResidenceCountryController,
l10n.countryOfTaxResidence),
_buildDropdownField(
_usPersonController, l10n.usPersonFlag, _yesNoOptions),
const Divider(height: 32),
_buildDropdownField(_pensionAmountController,
l10n.pensionAmount, _pensionOptions,
mandatory: true, onChanged: (val) {
setState(() {
_pensionAmountController.text = val ?? '1000';
});
}),
_buildDropdownField(_contributionTypeController,
l10n.periodicity, _getPeriodicityOptions(l10n),
mandatory: true, onChanged: (val) {
setState(() {
_contributionTypeController.text = val ?? 'C';
});
}),
_buildTextField(
_debitDateController, l10n.subsequentDebitDate,
mandatory: true),
],
),
),
),
const SizedBox(height: 16),
// Tile 2: Contribution Amount
Card(
elevation: 2,
color: Theme.of(context).colorScheme.secondaryContainer,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text(
'${l10n.contributionAmount} (${_getPeriodicityOptions(l10n)[_contributionTypeController.text]})',
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer)),
const SizedBox(height: 8),
Text('$_calculatedContribution',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer)),
],
),
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: _handleRegister,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(vertical: 16),
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
l10n.register,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 32),
],
),
),
),
);
}
Widget _buildDropdownField(
TextEditingController controller, String label, Map<String, String> options,
{bool readOnly = false, bool mandatory = false, void Function(String?)? onChanged}) {
String? currentValue =
options.containsKey(controller.text) ? controller.text : null;
return Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: DropdownButtonFormField<String>(
value: currentValue,
onChanged: readOnly ? null : (newValue) {
if (onChanged != null) {
onChanged(newValue);
} else {
setState(() {
controller.text = newValue ?? '';
});
}
},
decoration: InputDecoration(
labelText: mandatory ? '$label *' : label,
border: const OutlineInputBorder(),
contentPadding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
),
validator: (value) {
if (mandatory && (value == null || value.isEmpty)) {
return 'This field is required';
}
return null;
},
items: options.entries.map((entry) {
return DropdownMenuItem<String>(
value: entry.key,
child: Text(entry.value),
);
}).toList(),
),
);
}
Widget _buildTextField(TextEditingController controller, String label,
{TextInputType keyboardType = TextInputType.text,
bool readOnly = false,
bool mandatory = false}) {
return Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: TextFormField(
controller: controller,
readOnly: readOnly,
decoration: InputDecoration(
labelText: mandatory ? '$label *' : label,
border: const OutlineInputBorder(),
contentPadding:
const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
),
keyboardType: keyboardType,
validator: (value) {
if (mandatory && (value == null || value.isEmpty)) {
return 'This field is required';
}
return null;
},
),
);
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:kmobile/data/models/user.dart';
import 'package:kmobile/features/yojna/screens/apy_register_screen.dart';
import 'package:kmobile/l10n/app_localizations.dart';
class APYScreen extends StatefulWidget {
@@ -18,6 +19,7 @@ class APYScreen extends StatefulWidget {
class _APYScreenState extends State<APYScreen> {
User? _selectedAccount;
List<User> _filteredUsers = [];
final _formKey = GlobalKey<FormState>();
@override
void initState() {
@@ -54,58 +56,132 @@ class _APYScreenState extends State<APYScreen> {
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
l10n.apyDescription,
style: Theme.of(context).textTheme.titleMedium,
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
l10n.apyDescription,
style: Theme.of(context).textTheme.titleMedium,
),
),
),
),
const SizedBox(height: 16),
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DropdownButtonFormField<User>(
value: _selectedAccount,
decoration: InputDecoration(
labelText: l10n.accountNumber,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(
vertical: 20, horizontal: 12),
const SizedBox(height: 16),
Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DropdownButtonFormField<User>(
value: _selectedAccount,
decoration: InputDecoration(
labelText: l10n.accountNumber,
border: const OutlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(
vertical: 20, horizontal: 12),
),
items: _filteredUsers.map((user) {
return DropdownMenuItem<User>(
value: user,
child: Text(user.accountNo.toString()),
);
}).toList(),
onChanged: (User? newUser) {
setState(() {
_selectedAccount = newUser;
});
},
validator: (value) {
if (value == null) {
return l10n.accountNumberRequired;
}
return null;
},
),
items: _filteredUsers.map((user) {
return DropdownMenuItem<User>(
value: user,
child: Text(user.accountNo.toString()),
);
}).toList(),
onChanged: (User? newUser) {
setState(() {
_selectedAccount = newUser;
});
},
validator: (value) {
if (value == null) {
return l10n.accountNumberRequired;
}
return null;
},
),
],
],
),
),
),
),
],
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final mockData = {
"accountno": _selectedAccount?.accountNo ?? "50069506061",
"customerfirstname": "TAMANA",
"customermiddlename": "",
"customerlastname": "",
"availablebalance": "634000",
"customerdob": "06061998",
"emailid": "",
"gender": "F",
"married": "Y",
"nomineename": "shubham Kada",
"relationwithsubscriber": "S",
"mobilenumber": "",
"nomineeminor": "N",
"customerno": "30028309887",
"beneficaryofothersociatysecurityschemes": "N",
"whetherincometaxpayer": "N",
"customertitle": "02",
"aadharno": "",
"nameofspouse": "shubham kada",
"ageofjoining": "27",
"pensionamtoptedfor": "1000",
"montlycontributioncalculate": "90",
"collectionchannel": "1",
"subsequentContributionDebitDate": "02012026",
"secondnomineeminor": "N",
"secondnomineename": "shubham kad",
"secondrelationshipwithsubscriber": "",
"pincode": "176215",
"fatcacrsapplicable": "N",
"countryofbirth": "India",
"countryofcitizenship": "India",
"countryofresidencefortaxpurpose": "India",
"uspersonflag": "N",
"fatcadeclarationcount": "",
"documentevidencingcitizenshipflag": "",
"reasonfornoevidence": "",
"nameofdocumentforcitizenshipevidence": "",
"modeofcollection": "Transfer Voucher",
"contributionType": "C"
};
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => APYRegisterScreen(initialData: mockData),
),
);
}
},
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.primaryContainer,
foregroundColor:
Theme.of(context).colorScheme.onPrimaryContainer,
minimumSize: const Size(double.infinity, 50),
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
l10n.proceedButton,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
),
),
],
),
),
),
);