Proceed or Swipe to pay disabled on over limit

This commit is contained in:
2025-10-31 16:51:37 +05:30
parent d86ff2c427
commit 8f8fdb70e6
3 changed files with 148 additions and 110 deletions

View File

@@ -45,6 +45,7 @@ class _FundTransferAmountScreenState extends State<FundTransferAmountScreen> {
final _limitService = getIt<LimitService>(); final _limitService = getIt<LimitService>();
Limit? _limit; Limit? _limit;
bool _isLoadingLimit = true; bool _isLoadingLimit = true;
bool _isAmountOverLimit = false;
final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: ''); final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: '');
final _amountController = TextEditingController(); final _amountController = TextEditingController();
final _remarksController = TextEditingController(); final _remarksController = TextEditingController();
@@ -76,21 +77,28 @@ void initState() {
} }
// Add this method to check the amount against the limit // Add this method to check the amount against the limit
void _checkAmountLimit() { void _checkAmountLimit() {
if (_limit == null) return; if (_limit == null) return;
final amount = double.tryParse(_amountController.text) ?? 0; final amount = double.tryParse(_amountController.text) ?? 0;
final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit; final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit;
final bool isOverLimit = amount > remainingLimit;
if (amount > remainingLimit) { if (isOverLimit) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'), content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'),
backgroundColor: Colors.red, backgroundColor: Colors.red,
), ),
); );
} }
}
if (_isAmountOverLimit != isOverLimit) {
setState(() {
_isAmountOverLimit = isOverLimit;
});
}
}
@override @override
void dispose() { void dispose() {
@@ -490,15 +498,15 @@ void initState() {
// Proceed Button // Proceed Button
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: _onProceed, onPressed: _isAmountOverLimit ? null : _onProceed,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
), ),
child: Text(AppLocalizations.of(context).proceed), child: Text(AppLocalizations.of(context).proceed),
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
], ],
), ),

View File

@@ -46,6 +46,7 @@ final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: '₹');
final remarksController = TextEditingController(); final remarksController = TextEditingController();
final _ifscFocusNode = FocusNode(); final _ifscFocusNode = FocusNode();
final service = getIt<BeneficiaryService>(); final service = getIt<BeneficiaryService>();
bool _isAmountOverLimit = false;
late String accountType; late String accountType;
bool _isValidating = false; bool _isValidating = false;
@@ -88,21 +89,28 @@ Future<void> _loadLimit() async {
} }
// Add this method to check the amount against the limit // Add this method to check the amount against the limit
void _checkAmountLimit() { void _checkAmountLimit() {
if (_limit == null) return; if (_limit == null) return;
final amount = double.tryParse(amountController.text) ?? 0; final amount = double.tryParse(amountController.text) ?? 0;
final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit; final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit;
final bool isOverLimit = amount > remainingLimit;
if (amount > remainingLimit) { if (isOverLimit) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'), content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'),
backgroundColor: Colors.red, backgroundColor: Colors.red,
), ),
); );
} }
}
if (_isAmountOverLimit != isOverLimit) {
setState(() {
_isAmountOverLimit = isOverLimit;
});
}
}
void _validateIFSC() async { void _validateIFSC() async {
final ifsc = ifscController.text.trim().toUpperCase(); final ifsc = ifscController.text.trim().toUpperCase();
@@ -856,24 +864,31 @@ if (!_isLoadingLimit && _limit != null)
], ],
), ),
const SizedBox(height: 45), const SizedBox(height: 45),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: SwipeButton.expand( child: SwipeButton.expand(
thumb: Icon(Icons.arrow_forward, thumb: Icon(Icons.arrow_forward,
color: Theme.of(context).dialogBackgroundColor), color: _isAmountOverLimit ? Colors.grey : Theme.of(context).dialogBackgroundColor),
activeThumbColor: Theme.of(context).colorScheme.primary, activeThumbColor: _isAmountOverLimit ? Colors.grey.shade700 :
activeTrackColor: Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary.withAlpha(100), activeTrackColor: _isAmountOverLimit
borderRadius: BorderRadius.circular(30), ? Colors.grey.shade300
height: 56, : Theme.of(context).colorScheme.secondary.withAlpha(100),
onSwipe: _onProceedToPay, borderRadius: BorderRadius.circular(30),
child: Text( height: 56,
AppLocalizations.of(context).swipeToPay, onSwipe: () {
style: const TextStyle( if (_isAmountOverLimit) {
fontSize: 16, fontWeight: FontWeight.bold), return; // Do nothing if amount is over the limit
), }
), _onProceedToPay();
), },
child: Text(
AppLocalizations.of(context).swipeToPay,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
),
),
),
], ],
), ),
), ),

View File

@@ -31,7 +31,7 @@ final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: '₹');
final TextEditingController amountController = TextEditingController(); final TextEditingController amountController = TextEditingController();
final TextEditingController remarksController = TextEditingController(); final TextEditingController remarksController = TextEditingController();
String? _selectedAccountType; String? _selectedAccountType;
bool _isAmountOverLimit = false;
String? _beneficiaryName; String? _beneficiaryName;
bool _isValidating = false; bool _isValidating = false;
bool _isBeneficiaryValidated = false; bool _isBeneficiaryValidated = false;
@@ -43,6 +43,7 @@ final _formatCurrency = NumberFormat.currency(locale: 'en_IN', symbol: '₹');
_loadLimit(); _loadLimit();
accountNumberController.addListener(_resetBeneficiaryValidation); accountNumberController.addListener(_resetBeneficiaryValidation);
confirmAccountNumberController.addListener(_resetBeneficiaryValidation); confirmAccountNumberController.addListener(_resetBeneficiaryValidation);
amountController.addListener(_checkAmountLimit);
} }
Future<void> _loadLimit() async { Future<void> _loadLimit() async {
@@ -68,8 +69,9 @@ void _checkAmountLimit() {
final amount = double.tryParse(amountController.text) ?? 0; final amount = double.tryParse(amountController.text) ?? 0;
final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit; final remainingLimit = _limit!.dailyLimit - _limit!.usedLimit;
final bool isOverLimit = amount > remainingLimit;
if (amount > remainingLimit) { if (isOverLimit) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'), content: Text('Amount exceeds remaining daily limit of ${_formatCurrency.format(remainingLimit)}'),
@@ -77,6 +79,13 @@ void _checkAmountLimit() {
), ),
); );
} }
// Update state only if it changes to avoid unnecessary rebuilds
if (_isAmountOverLimit != isOverLimit) {
setState(() {
_isAmountOverLimit = isOverLimit;
});
}
} }
void _resetBeneficiaryValidation() { void _resetBeneficiaryValidation() {
@@ -379,63 +388,69 @@ if (!_isLoadingLimit && _limit != null)
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
const SizedBox(height: 45), const SizedBox(height: 45),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: SwipeButton.expand( child: SwipeButton.expand(
thumb: Icon(Icons.arrow_forward, thumb: Icon(Icons.arrow_forward,
color: Theme.of(context).dialogBackgroundColor), color: _isAmountOverLimit ? Colors.grey : Theme.of(context).dialogBackgroundColor),
activeThumbColor: Theme.of(context).colorScheme.primary, activeThumbColor: _isAmountOverLimit ? Colors.grey.shade700 :
activeTrackColor: Theme.of( Theme.of(context).colorScheme.primary,
context, activeTrackColor: _isAmountOverLimit
).colorScheme.secondary.withAlpha(100), ? Colors.grey.shade300
borderRadius: BorderRadius.circular(30), : Theme.of(
height: 56, context,
child: Text( ).colorScheme.secondary.withAlpha(100),
AppLocalizations.of(context).swipeToPay, borderRadius: BorderRadius.circular(30),
style: const TextStyle(fontSize: 16), height: 56,
), child: Text(
onSwipe: () { AppLocalizations.of(context).swipeToPay,
if (_formKey.currentState!.validate()) { style: const TextStyle(fontSize: 16),
if (!_isBeneficiaryValidated) { ),
setState(() { onSwipe: () {
_validationError = AppLocalizations.of(context) if (_isAmountOverLimit) {
.validateBeneficiaryproceeding; return; // Do nothing if amount is over limit
}); }
return; if (_formKey.currentState!.validate()) {
} if (!_isBeneficiaryValidated) {
// Perform payment logic setState(() {
Navigator.push( _validationError = AppLocalizations.of(context)
context, .validateBeneficiaryproceeding;
MaterialPageRoute( });
builder: (context) => TransactionPinScreen( return;
onPinCompleted: (pinScreenContext, tpin) async { }
final transfer = Transfer( // Perform payment logic
fromAccount: widget.debitAccount, Navigator.push(
toAccount: accountNumberController.text, context,
toAccountType: _selectedAccountType!, MaterialPageRoute(
amount: amountController.text, builder: (context) => TransactionPinScreen(
tpin: tpin, onPinCompleted: (pinScreenContext, tpin) async {
remarks: remarksController.text, final transfer = Transfer(
); fromAccount: widget.debitAccount,
toAccount: accountNumberController.text,
toAccountType: _selectedAccountType!,
amount: amountController.text,
tpin: tpin,
remarks: remarksController.text,
);
final paymentService = getIt<PaymentService>(); final paymentService = getIt<PaymentService>();
final paymentResponseFuture = paymentService final paymentResponseFuture = paymentService
.processQuickPayWithinBank(transfer); .processQuickPayWithinBank(transfer);
Navigator.of(pinScreenContext).pushReplacement( Navigator.of(pinScreenContext).pushReplacement(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => PaymentAnimationScreen( builder: (_) => PaymentAnimationScreen(
paymentResponse: paymentResponseFuture), paymentResponse: paymentResponseFuture),
), ),
); );
}, },
), ),
), ),
); );
} }
}, },
), ),
), ),
], ],
), ),
), ),