715 lines
26 KiB
Dart
715 lines
26 KiB
Dart
import 'package:dio/dio.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:kmobile/api/services/deposit_service.dart';
|
|
import 'package:kmobile/data/models/user.dart';
|
|
import 'package:kmobile/di/injection.dart';
|
|
import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart';
|
|
import 'package:kmobile/l10n/app_localizations.dart';
|
|
|
|
class CreateDepositScreen extends StatefulWidget {
|
|
final User selectedAccount;
|
|
final Map<String, dynamic>? initialData;
|
|
const CreateDepositScreen({
|
|
super.key,
|
|
required this.selectedAccount,
|
|
this.initialData,
|
|
});
|
|
|
|
@override
|
|
State<CreateDepositScreen> createState() => _CreateDepositScreenState();
|
|
}
|
|
|
|
class _CreateDepositScreenState extends State<CreateDepositScreen> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
bool _isLoading = false;
|
|
|
|
// Controllers
|
|
late final _fromAcctNoController =
|
|
TextEditingController(text: widget.selectedAccount.accountNo.toString());
|
|
late final _cifNoController = TextEditingController(
|
|
text: widget.initialData?['cifNo']?.toString());
|
|
late final _idNoController = TextEditingController(
|
|
text: widget.initialData?['idno']?.toString());
|
|
late final _customerNameController = TextEditingController(
|
|
text: widget.initialData?['customername']?.toString());
|
|
late final _nationalityController = TextEditingController(
|
|
text: widget.initialData?['nationality']?.toString());
|
|
late final _addressLine1Controller = TextEditingController(
|
|
text: widget.initialData?['addressline1']?.toString());
|
|
late final _addressLine2Controller = TextEditingController(
|
|
text: widget.initialData?['addressline2']?.toString());
|
|
late final _pincodeController = TextEditingController(
|
|
text: widget.initialData?['pincode']?.toString());
|
|
|
|
late final _productController = TextEditingController();
|
|
late final _typeController = TextEditingController();
|
|
late final _customerCategoryController = TextEditingController();
|
|
late final _termLocationController = TextEditingController();
|
|
late final _currencyController = TextEditingController();
|
|
late final _acctSgmtCodeController = TextEditingController();
|
|
late final _interestPaymentMethodController = TextEditingController();
|
|
late final _taxFileNumberIndicatorController = TextEditingController();
|
|
late final _termLengthController = TextEditingController();
|
|
late final _termBasisController = TextEditingController();
|
|
late final _termValueDepositedController = TextEditingController();
|
|
late final _interestFrequencyController = TextEditingController();
|
|
late final _termDaysController = TextEditingController();
|
|
late final _termMonthsController = TextEditingController();
|
|
late final _termYearsController = TextEditingController();
|
|
late final _nominationRequiredController = TextEditingController();
|
|
late final _printNomineeNameController = TextEditingController();
|
|
|
|
// RD specific controllers
|
|
late final _rdInstallmentValueController = TextEditingController();
|
|
late final _monthlyRDInstallmentDueDayController = TextEditingController();
|
|
late final _rdInstlFreqController = TextEditingController();
|
|
|
|
Map<String, String> get _productOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'20': l10n.termDepositOption,
|
|
'25': l10n.fixedDepositOption,
|
|
'28': l10n.recurringDepositOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _typeOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'01': l10n.memberOption,
|
|
'02': l10n.nonMemberOption,
|
|
'03': l10n.staffOption,
|
|
'04': l10n.govtOption,
|
|
'05': l10n.schoolOption,
|
|
'06': l10n.panchayatOption,
|
|
'07': l10n.trustsOption,
|
|
'08': l10n.municipalCouncilOption,
|
|
'09': l10n.nroOption,
|
|
'10': l10n.bankOption,
|
|
'11': l10n.noFrillOption,
|
|
'12': l10n.specialSchemesOption,
|
|
'13': l10n.minorOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _customerCategoryOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'1': l10n.publicIndividualOption,
|
|
'2': l10n.societiesOption,
|
|
'3': l10n.seniorCitizenOption,
|
|
'5': l10n.governmentOption,
|
|
'6': l10n.localBodiesOption,
|
|
'7': l10n.otherOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _termLocationOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'10': l10n.sbCaOption,
|
|
'13': l10n.days46_90Option,
|
|
'14': l10n.days91_180Option,
|
|
'15': l10n.days180_1YearOption,
|
|
'16': l10n.year1_18MonthsOption,
|
|
'20': l10n.months18_2YearsOption,
|
|
'17': l10n.years2_3Option,
|
|
'18': l10n.years3_10Option,
|
|
'22': l10n.above10YearsOption,
|
|
'24': l10n.devKanyaYojnaOption,
|
|
'25': l10n.hazarDainLakhOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _currencyOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'1': l10n.currencyINR,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _acctSgmtCodeOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'0706': l10n.publicIndividualOption,
|
|
'5002': l10n.societiesOption,
|
|
'5005': l10n.governmentOption,
|
|
'5050': l10n.glOthersOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _interestPaymentMethodOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'R': l10n.reInvestOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _termBasisOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'D': l10n.daysOption,
|
|
'M': l10n.monthsOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _interestFrequencyOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'M': l10n.onMaturityOption,
|
|
'1M': l10n.monthlyOption,
|
|
'3M': l10n.quarterlyOption,
|
|
'1A': l10n.anniversaryMonthlyOption,
|
|
'3A': l10n.anniversaryQuarterlyOption,
|
|
};
|
|
}
|
|
|
|
Map<String, String> get _rdInstlFreqOptions {
|
|
final l10n = AppLocalizations.of(context);
|
|
return {
|
|
'M': l10n.monthlyOption,
|
|
};
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_fromAcctNoController.dispose();
|
|
_cifNoController.dispose();
|
|
_idNoController.dispose();
|
|
_customerNameController.dispose();
|
|
_nationalityController.dispose();
|
|
_addressLine1Controller.dispose();
|
|
_addressLine2Controller.dispose();
|
|
_pincodeController.dispose();
|
|
_productController.dispose();
|
|
_typeController.dispose();
|
|
_customerCategoryController.dispose();
|
|
_termLocationController.dispose();
|
|
_currencyController.dispose();
|
|
_acctSgmtCodeController.dispose();
|
|
_interestPaymentMethodController.dispose();
|
|
_taxFileNumberIndicatorController.dispose();
|
|
_termLengthController.dispose();
|
|
_termBasisController.dispose();
|
|
_termValueDepositedController.dispose();
|
|
_interestFrequencyController.dispose();
|
|
_termDaysController.dispose();
|
|
_termMonthsController.dispose();
|
|
_termYearsController.dispose();
|
|
_nominationRequiredController.dispose();
|
|
_printNomineeNameController.dispose();
|
|
_rdInstallmentValueController.dispose();
|
|
_monthlyRDInstallmentDueDayController.dispose();
|
|
_rdInstlFreqController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _handleCreate() {
|
|
if (_formKey.currentState!.validate()) {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => TransactionPinScreen(
|
|
onPinCompleted: (ctx, pin) async {
|
|
Navigator.pop(context);
|
|
_executeCreation(pin);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _executeCreation(String pin) async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final product = _productController.text;
|
|
dynamic response;
|
|
|
|
if (product == '20' || product == '25') {
|
|
response = await getIt<DepositService>().createFdTd(
|
|
fromacctno: _fromAcctNoController.text,
|
|
cifNo: _cifNoController.text,
|
|
idno: _idNoController.text,
|
|
customername: _customerNameController.text,
|
|
nationality: _nationalityController.text,
|
|
addressline1: _addressLine1Controller.text,
|
|
addressline2: _addressLine2Controller.text,
|
|
pincode: _pincodeController.text,
|
|
product: _productController.text,
|
|
type: _typeController.text,
|
|
customercategory: _customerCategoryController.text,
|
|
termlocation: _termLocationController.text,
|
|
currency: _currencyController.text,
|
|
acctsgmtcode: _acctSgmtCodeController.text,
|
|
interestpaymentmethod: _interestPaymentMethodController.text,
|
|
taxfilenumberindicator: _taxFileNumberIndicatorController.text,
|
|
termlenght: _termLengthController.text,
|
|
termbasis: _termBasisController.text,
|
|
termvaluedeposited: _termValueDepositedController.text,
|
|
interestfrequency: _interestFrequencyController.text,
|
|
termdays: _termDaysController.text,
|
|
termmonths: _termMonthsController.text,
|
|
termyears: _termYearsController.text,
|
|
nominationRequired: _nominationRequiredController.text,
|
|
printNomineename: _printNomineeNameController.text,
|
|
tpin: pin,
|
|
);
|
|
} else if (product == '28') {
|
|
response = await getIt<DepositService>().createRd(
|
|
fromacctno: _fromAcctNoController.text,
|
|
cifNo: _cifNoController.text,
|
|
idno: _idNoController.text,
|
|
customername: _customerNameController.text,
|
|
nationality: _nationalityController.text,
|
|
addressline1: _addressLine1Controller.text,
|
|
addressline2: _addressLine2Controller.text,
|
|
pincode: _pincodeController.text,
|
|
product: _productController.text,
|
|
type: _typeController.text,
|
|
customercategory: _customerCategoryController.text,
|
|
termlocation: _termLocationController.text,
|
|
currency: _currencyController.text,
|
|
acctsgmtcode: _acctSgmtCodeController.text,
|
|
interestpaymentmethod: _interestPaymentMethodController.text,
|
|
taxfilenumberindicator: _taxFileNumberIndicatorController.text,
|
|
termlenght: _termLengthController.text,
|
|
termbasis: _termBasisController.text,
|
|
termvaluedeposited: _termValueDepositedController.text,
|
|
interestfrequency: _interestFrequencyController.text,
|
|
termdays: _termDaysController.text,
|
|
termmonths: _termMonthsController.text,
|
|
termyears: _termYearsController.text,
|
|
nominationRequired: _nominationRequiredController.text,
|
|
printNomineename: _printNomineeNameController.text,
|
|
rdinstallmentvalue: _rdInstallmentValueController.text,
|
|
monthlyRDInstallmentdueday: _monthlyRDInstallmentDueDayController.text,
|
|
rdInstlFreq: _rdInstlFreqController.text,
|
|
tpin: pin,
|
|
);
|
|
}
|
|
|
|
if (response != null && response is Map<String, dynamic>) {
|
|
if (mounted) {
|
|
final l10n = AppLocalizations.of(context);
|
|
if (response['error'] == 'INCORRECT_TPIN' || response['message'] == 'INCORRECT_TPIN') {
|
|
_showSimpleDialog(l10n.invalidTpin, l10n.incorrectTpinMessage);
|
|
} else if (response['status'] == 'FAILED') {
|
|
_showFailureDialog(response['message']?.toString());
|
|
} else if (response.containsKey('accountno')) {
|
|
_showResponseDialog(response);
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(l10n.unexpectedResponse)),
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
if (mounted) {
|
|
final l10n = AppLocalizations.of(context);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(l10n.failedToCreateDeposit)),
|
|
);
|
|
}
|
|
}
|
|
} on DioException catch (e) {
|
|
if (mounted) {
|
|
final l10n = AppLocalizations.of(context);
|
|
try {
|
|
final errorBody = e.response?.data;
|
|
if (errorBody is Map && (errorBody['error'] == 'INCORRECT_TPIN' || errorBody['message'] == 'INCORRECT_TPIN')) {
|
|
_showSimpleDialog(l10n.invalidTpin, l10n.incorrectTpinMessage);
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text("${l10n.error}: ${e.message}")),
|
|
);
|
|
}
|
|
} catch (_) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(l10n.internalServerError)),
|
|
);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
final l10n = AppLocalizations.of(context);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text("${l10n.error}: $e")),
|
|
);
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _showSimpleDialog(String title, String message) async {
|
|
final l10n = AppLocalizations.of(context);
|
|
return showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text(title),
|
|
content: SingleChildScrollView(
|
|
child: ListBody(
|
|
children: <Widget>[
|
|
Text(message),
|
|
],
|
|
),
|
|
),
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: Text(l10n.ok),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void _showFailureDialog(String? apiMessage) {
|
|
final l10n = AppLocalizations.of(context);
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Row(
|
|
children: [
|
|
Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error),
|
|
const SizedBox(width: 8),
|
|
Text(l10n.creationFailed),
|
|
],
|
|
),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
l10n.accountCreationFailed,
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(height: 12),
|
|
if (apiMessage != null) ...[
|
|
Text(l10n.reason(apiMessage)),
|
|
const SizedBox(height: 12),
|
|
],
|
|
Text(
|
|
l10n.creationFailedDescription,
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: Text(l10n.ok),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showResponseDialog(Map<String, dynamic> response) {
|
|
final l10n = AppLocalizations.of(context);
|
|
final accountNo = response['accountno']?.toString() ?? 'N/A';
|
|
final accountTypeRaw = response['accounttype']?.toString() ?? '';
|
|
final accountTypeText = _productOptions[accountTypeRaw] ?? accountTypeRaw;
|
|
final amount = response['amount']?.toString() ?? '0.0';
|
|
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => AlertDialog(
|
|
title: Text(l10n.depositCreatedSuccessfully),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("${l10n.accountNumberLabel}: $accountNo"),
|
|
const SizedBox(height: 8),
|
|
Text("${l10n.accountTypeLabel}: $accountTypeText"),
|
|
const SizedBox(height: 8),
|
|
Text(l10n.amountValue(amount)),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
|
},
|
|
child: Text(l10n.ok),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context);
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(l10n.createDeposit),
|
|
),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
// Tile 1: Customer Info
|
|
Card(
|
|
elevation: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l10n.customerInformation,
|
|
style: const TextStyle(
|
|
fontSize: 18, fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 16),
|
|
_buildTextField(_fromAcctNoController, l10n.fromAccountNo,
|
|
mandatory: true, readOnly: true),
|
|
_buildTextField(_cifNoController, l10n.cifNo),
|
|
_buildTextField(_idNoController, l10n.idNo),
|
|
_buildTextField(_customerNameController, l10n.customerName),
|
|
_buildTextField(_nationalityController, l10n.nationality),
|
|
_buildTextField(_addressLine1Controller, l10n.addressLine1),
|
|
_buildTextField(_addressLine2Controller, l10n.addressLine2),
|
|
_buildTextField(_pincodeController, l10n.pincodeLabel,
|
|
keyboardType: TextInputType.number),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
// Tile 2: Deposit Details
|
|
Card(
|
|
elevation: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l10n.depositDetails,
|
|
style: const TextStyle(
|
|
fontSize: 18, fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 16),
|
|
_buildDropdownField(
|
|
_productController, l10n.product, _productOptions,
|
|
mandatory: true, onChanged: (val) {
|
|
setState(() {
|
|
_productController.text = val ?? '';
|
|
});
|
|
}),
|
|
_buildDropdownField(_typeController, l10n.type, _typeOptions,
|
|
mandatory: true),
|
|
_buildDropdownField(_customerCategoryController,
|
|
l10n.customerCategory, _customerCategoryOptions,
|
|
mandatory: true),
|
|
_buildDropdownField(_termLocationController,
|
|
l10n.termLocation, _termLocationOptions,
|
|
mandatory: true),
|
|
_buildDropdownField(
|
|
_currencyController, l10n.currency, _currencyOptions,
|
|
mandatory: true),
|
|
_buildDropdownField(_acctSgmtCodeController,
|
|
l10n.accountSegmentCode, _acctSgmtCodeOptions,
|
|
mandatory: true),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
// Tile 3: Interest & Term Settings
|
|
Card(
|
|
elevation: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l10n.interestTermSettings,
|
|
style: const TextStyle(
|
|
fontSize: 18, fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 16),
|
|
_buildDropdownField(_interestPaymentMethodController,
|
|
l10n.interestPaymentMethod, _interestPaymentMethodOptions,
|
|
mandatory: true),
|
|
_buildTextField(_taxFileNumberIndicatorController,
|
|
l10n.taxFileNumberIndicator),
|
|
_buildTextField(_termLengthController, l10n.termLength),
|
|
_buildDropdownField(
|
|
_termBasisController, l10n.termBasis, _termBasisOptions,
|
|
mandatory: true),
|
|
_buildTextField(_termValueDepositedController,
|
|
l10n.termValueDeposited,
|
|
keyboardType: TextInputType.number,
|
|
mandatory: _productController.text != '28'),
|
|
_buildDropdownField(_interestFrequencyController,
|
|
l10n.interestFrequency, _interestFrequencyOptions),
|
|
_buildTextField(_termDaysController, l10n.termDays,
|
|
keyboardType: TextInputType.number),
|
|
_buildTextField(_termMonthsController, l10n.termMonths,
|
|
keyboardType: TextInputType.number),
|
|
_buildTextField(_termYearsController, l10n.termYears,
|
|
keyboardType: TextInputType.number, mandatory: true),
|
|
_buildTextField(
|
|
_nominationRequiredController, l10n.nominationRequired),
|
|
_buildTextField(
|
|
_printNomineeNameController, l10n.printNomineeName),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Tile 4: RD Specific Fields (Conditional)
|
|
if (_productController.text == '28')
|
|
Card(
|
|
elevation: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(l10n.recurringDepositSettings,
|
|
style: const TextStyle(
|
|
fontSize: 18, fontWeight: FontWeight.bold)),
|
|
const SizedBox(height: 16),
|
|
_buildTextField(_rdInstallmentValueController,
|
|
l10n.rdInstallmentValue,
|
|
keyboardType: TextInputType.number),
|
|
_buildTextField(_monthlyRDInstallmentDueDayController,
|
|
l10n.monthlyRDInstallmentDueDay,
|
|
keyboardType: TextInputType.number),
|
|
_buildDropdownField(_rdInstlFreqController,
|
|
l10n.rdInstallmentFrequency, _rdInstlFreqOptions),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 24),
|
|
ElevatedButton(
|
|
onPressed: _isLoading ? null : _handleCreate,
|
|
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: _isLoading
|
|
? const SizedBox(
|
|
height: 20,
|
|
width: 20,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2,
|
|
),
|
|
)
|
|
: Text(
|
|
l10n.proceedButton,
|
|
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}) {
|
|
final l10n = AppLocalizations.of(context);
|
|
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 || _isLoading
|
|
? null
|
|
: (newValue) {
|
|
if (newValue != null) {
|
|
setState(() {
|
|
controller.text = newValue;
|
|
});
|
|
}
|
|
if (onChanged != null) {
|
|
onChanged(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 l10n.fieldRequired;
|
|
}
|
|
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,
|
|
VoidCallback? onTap}) {
|
|
final l10n = AppLocalizations.of(context);
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16.0),
|
|
child: TextFormField(
|
|
controller: controller,
|
|
readOnly: readOnly || onTap != null || _isLoading,
|
|
onTap: _isLoading ? null : onTap,
|
|
decoration: InputDecoration(
|
|
labelText: mandatory ? '$label *' : label,
|
|
border: const OutlineInputBorder(),
|
|
suffixIcon: onTap != null ? const Icon(Icons.calendar_today) : null,
|
|
contentPadding:
|
|
const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
|
|
),
|
|
keyboardType: keyboardType,
|
|
validator: (value) {
|
|
if (mandatory && (value == null || value.isEmpty)) {
|
|
return l10n.fieldRequired;
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|