changed imports of AppLocalization

Also added beneficiary validation while quick pay within bank
This commit is contained in:
2025-07-24 22:21:19 +05:30
parent 23d742ace9
commit 787fcdc2e2
42 changed files with 3965 additions and 1025 deletions

View File

@@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
import 'package:kmobile/features/fund_transfer/screens/fund_transfer_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
class FundTransferBeneficiaryScreen extends StatefulWidget {
const FundTransferBeneficiaryScreen({super.key});
@@ -16,22 +16,10 @@ class FundTransferBeneficiaryScreen extends StatefulWidget {
class _FundTransferBeneficiaryScreen
extends State<FundTransferBeneficiaryScreen> {
final List<Map<String, String>> beneficiaries = [
{
'bank': 'State Bank Of India',
'name': 'Trina Bakshi',
},
{
'bank': 'State Bank Of India',
'name': 'Sheetal Rao',
},
{
'bank': 'Punjab National Bank',
'name': 'Manoj Kumar',
},
{
'bank': 'State Bank Of India',
'name': 'Rohit Mehra',
},
{'bank': 'State Bank Of India', 'name': 'Trina Bakshi'},
{'bank': 'State Bank Of India', 'name': 'Sheetal Rao'},
{'bank': 'Punjab National Bank', 'name': 'Manoj Kumar'},
{'bank': 'State Bank Of India', 'name': 'Rohit Mehra'},
];
@override
@@ -73,23 +61,24 @@ class _FundTransferBeneficiaryScreen
final beneficiary = beneficiaries[index];
return ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.blue, child: Text('A')),
backgroundColor: Colors.blue,
child: Text('A'),
),
title: Text(beneficiary['name']!),
subtitle: Text(beneficiary['bank']!),
trailing: IconButton(
icon: const Icon(
Symbols.arrow_right,
size: 20,
),
icon: const Icon(Symbols.arrow_right, size: 20),
onPressed: () {
// Delete action
},
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const FundTransferScreen()));
context,
MaterialPageRoute(
builder: (context) => const FundTransferScreen(),
),
);
},
);
},
@@ -100,9 +89,11 @@ class _FundTransferBeneficiaryScreen
child: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddBeneficiaryScreen()));
context,
MaterialPageRoute(
builder: (context) => const AddBeneficiaryScreen(),
),
);
},
backgroundColor: Colors.grey[300],
foregroundColor: Colors.blue[900],

View File

@@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
class FundTransferScreen extends StatefulWidget {
const FundTransferScreen({super.key});
@@ -45,14 +45,17 @@ class _FundTransferScreen extends State<FundTransferScreen> {
);
} else if (value == 'back') {
return GestureDetector(
onTap: () => onKeyTap(value),
child: const Icon(Symbols.backspace, size: 30));
onTap: () => onKeyTap(value),
child: const Icon(Symbols.backspace, size: 30),
);
} else {
return GestureDetector(
onTap: () => onKeyTap(value),
child: Center(
child: Text(value,
style: const TextStyle(fontSize: 24, color: Colors.black)),
child: Text(
value,
style: const TextStyle(fontSize: 24, color: Colors.black),
),
),
);
}
@@ -109,12 +112,14 @@ class _FundTransferScreen extends State<FundTransferScreen> {
Text(
'0300015678903456',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
)
),
],
),
const SizedBox(height: 20),
Text(AppLocalizations.of(context).enterAmount,
style: TextStyle(fontSize: 20)),
Text(
AppLocalizations.of(context).enterAmount,
style: TextStyle(fontSize: 20),
),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),

View File

@@ -7,15 +7,12 @@ import 'package:kmobile/data/models/payment_response.dart';
import 'package:lottie/lottie.dart';
import 'package:share_plus/share_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
class PaymentAnimationScreen extends StatefulWidget {
final Future<PaymentResponse> paymentResponse;
const PaymentAnimationScreen({
super.key,
required this.paymentResponse,
});
const PaymentAnimationScreen({super.key, required this.paymentResponse});
@override
State<PaymentAnimationScreen> createState() => _PaymentAnimationScreenState();
@@ -29,22 +26,26 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
RenderRepaintBoundary boundary =
_shareKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
ByteData? byteData = await image.toByteData(
format: ui.ImageByteFormat.png,
);
Uint8List pngBytes = byteData!.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final file = await File('${tempDir.path}/payment_result.png').create();
await file.writeAsBytes(pngBytes);
await Share.shareXFiles([XFile(file.path)],
text: '${AppLocalizations.of(context).paymentResult}');
await Share.shareXFiles([
XFile(file.path),
], text: '${AppLocalizations.of(context).paymentResult}');
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'${AppLocalizations.of(context).failedToShareScreenshot}: $e')),
content: Text(
'${AppLocalizations.of(context).failedToShareScreenshot}: $e',
),
),
);
}
}
@@ -93,29 +94,33 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
? Column(
children: [
Text(
AppLocalizations.of(context)
.paymentSuccessful,
AppLocalizations.of(
context,
).paymentSuccessful,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.green),
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const SizedBox(height: 16),
if (response.amount != null)
Text(
'${AppLocalizations.of(context).amount}: ${response.amount} ${response.currency ?? ''}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
fontFamily: 'Rubik'),
fontSize: 18,
fontWeight: FontWeight.w700,
fontFamily: 'Rubik',
),
),
if (response.creditedAccount != null)
Text(
'${AppLocalizations.of(context).creditedAccount}: ${response.creditedAccount}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: 'Rubik'),
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: 'Rubik',
),
),
if (response.date != null)
Text(
@@ -129,9 +134,10 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
Text(
AppLocalizations.of(context).paymentFailed,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.red),
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const SizedBox(height: 16),
if (response.errorMessage != null)
@@ -161,14 +167,18 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
Icons.share_rounded,
color: Theme.of(context).primaryColor,
),
label: Text(AppLocalizations.of(context).share,
style:
TextStyle(color: Theme.of(context).primaryColor)),
label: Text(
AppLocalizations.of(context).share,
style: TextStyle(color: Theme.of(context).primaryColor),
),
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
backgroundColor: Theme.of(
context,
).scaffoldBackgroundColor,
padding: const EdgeInsets.symmetric(
horizontal: 32, vertical: 12),
horizontal: 32,
vertical: 12,
),
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).primaryColor,
@@ -177,25 +187,31 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
borderRadius: BorderRadius.circular(30),
),
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black),
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
ElevatedButton.icon(
onPressed: () {
Navigator.of(context)
.popUntil((route) => route.isFirst);
Navigator.of(
context,
).popUntil((route) => route.isFirst);
},
label: Text(AppLocalizations.of(context).done),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 45, vertical: 12),
horizontal: 45,
vertical: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
textStyle: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600),
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
],

View File

@@ -1,4 +1,4 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:kmobile/features/fund_transfer/screens/tpin_set_screen.dart';
@@ -12,8 +12,10 @@ class TpinOtpScreen extends StatefulWidget {
class _TpinOtpScreenState extends State<TpinOtpScreen> {
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
final List<TextEditingController> _controllers =
List.generate(4, (_) => TextEditingController());
final List<TextEditingController> _controllers = List.generate(
4,
(_) => TextEditingController(),
);
@override
void dispose() {
@@ -42,9 +44,7 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
if (_enteredOtp == '0000') {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => TpinSetScreen(),
),
MaterialPageRoute(builder: (_) => TpinSetScreen()),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
@@ -67,8 +67,11 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
child: Column(
// mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.lock_outline,
size: 48, color: theme.colorScheme.primary),
Icon(
Icons.lock_outline,
size: 48,
color: theme.colorScheme.primary,
),
const SizedBox(height: 16),
Text(
AppLocalizations.of(context).otpVerification,
@@ -133,8 +136,10 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
),
style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
padding:
const EdgeInsets.symmetric(vertical: 14, horizontal: 28),
padding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 28,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
@@ -147,7 +152,8 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
// Resend OTP logic here
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context).otpResent)),
content: Text(AppLocalizations.of(context).otpResent),
),
);
},
child: Text(AppLocalizations.of(context).resendOtp),

View File

@@ -1,4 +1,4 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:kmobile/features/fund_transfer/screens/tpin_otp_screen.dart';
@@ -10,16 +10,17 @@ class TpinSetupPromptScreen extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).setTpin),
),
appBar: AppBar(title: Text(AppLocalizations.of(context).setTpin)),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 100.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.lock_person_rounded,
size: 60, color: theme.colorScheme.primary),
Icon(
Icons.lock_person_rounded,
size: 60,
color: theme.colorScheme.primary,
),
const SizedBox(height: 18),
Text(
AppLocalizations.of(context).tpinRequired,
@@ -45,8 +46,10 @@ class TpinSetupPromptScreen extends StatelessWidget {
),
style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary,
padding:
const EdgeInsets.symmetric(vertical: 14, horizontal: 32),
padding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 32,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
@@ -54,9 +57,7 @@ class TpinSetupPromptScreen extends StatelessWidget {
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const TpinOtpScreen(),
),
MaterialPageRoute(builder: (_) => const TpinOtpScreen()),
);
},
),

View File

@@ -1,4 +1,4 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:kmobile/api/services/auth_service.dart';
@@ -66,14 +66,17 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
context: context,
barrierDismissible: false,
builder: (ctx) => AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
title: Column(
children: [
Icon(Icons.check_circle, color: Colors.green, size: 60),
SizedBox(height: 12),
Text(AppLocalizations.of(context).success,
style: TextStyle(fontWeight: FontWeight.bold)),
Text(
AppLocalizations.of(context).success,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
content: Text(
@@ -85,8 +88,10 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text(AppLocalizations.of(context).ok,
style: TextStyle(fontSize: 16)),
child: Text(
AppLocalizations.of(context).ok,
style: TextStyle(fontSize: 16),
),
),
],
),
@@ -125,7 +130,7 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
['1', '2', '3'],
['4', '5', '6'],
['7', '8', '9'],
['Enter', '0', '<']
['Enter', '0', '<'],
];
return Column(
@@ -144,8 +149,9 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
_handleComplete();
} else {
setState(() {
_errorText =
AppLocalizations.of(context).pleaseEnter6Digits;
_errorText = AppLocalizations.of(
context,
).pleaseEnter6Digits;
});
}
} else if (key.isNotEmpty) {
@@ -206,8 +212,10 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
if (_errorText != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(_errorText!,
style: const TextStyle(color: Colors.red)),
child: Text(
_errorText!,
style: const TextStyle(color: Colors.red),
),
),
const Spacer(),
buildNumberPad(),

View File

@@ -1,4 +1,4 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
import 'package:flutter/material.dart';
import 'package:kmobile/api/services/auth_service.dart';
@@ -36,9 +36,7 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
if (!isSet && mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const TpinSetupPromptScreen(),
),
MaterialPageRoute(builder: (_) => const TpinSetupPromptScreen()),
);
} else if (mounted) {
setState(() => _loading = false);
@@ -48,7 +46,8 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
setState(() => _loading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context).tpinStatusFailed)),
content: Text(AppLocalizations.of(context).tpinStatusFailed),
),
);
}
}
@@ -95,8 +94,8 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text(AppLocalizations.of(context).enter6DigitTpin)),
content: Text(AppLocalizations.of(context).enter6DigitTpin),
),
);
}
} else {
@@ -122,11 +121,13 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
Row(children: [_buildKey('1'), _buildKey('2'), _buildKey('3')]),
Row(children: [_buildKey('4'), _buildKey('5'), _buildKey('6')]),
Row(children: [_buildKey('7'), _buildKey('8'), _buildKey('9')]),
Row(children: [
_buildKey('done', icon: Icons.check),
_buildKey('0'),
_buildKey('back', icon: Icons.backspace_outlined),
]),
Row(
children: [
_buildKey('done', icon: Icons.check),
_buildKey('0'),
_buildKey('back', icon: Icons.backspace_outlined),
],
),
],
);
}
@@ -136,19 +137,21 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
final transfer = widget.transactionData;
transfer.tpin = _pin.join();
try {
final paymentResponse =
paymentService.processQuickPayWithinBank(transfer);
final paymentResponse = paymentService.processQuickPayWithinBank(
transfer,
);
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) =>
PaymentAnimationScreen(paymentResponse: paymentResponse)),
builder: (_) =>
PaymentAnimationScreen(paymentResponse: paymentResponse),
),
);
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(e.toString())));
}
}
}

View File

@@ -1,4 +1,4 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../../../l10n/app_localizations.dart';
import 'dart:io';
import 'dart:typed_data';
@@ -35,8 +35,9 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
@override
Widget build(BuildContext context) {
final String transactionDate =
DateTime.now().toLocal().toString().split(' ')[0];
final String transactionDate = DateTime.now().toLocal().toString().split(
' ',
)[0];
final String creditAccount = widget.creditAccount;
return Scaffold(
@@ -52,37 +53,24 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
const CircleAvatar(
radius: 50,
backgroundColor: Colors.blue,
child: Icon(
Icons.check,
color: Colors.white,
size: 60,
),
child: Icon(Icons.check, color: Colors.white, size: 60),
),
const SizedBox(height: 24),
Text(
AppLocalizations.of(context).transactionSuccess,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
Text(
"On $transactionDate",
style: const TextStyle(
fontSize: 14,
color: Colors.black54,
),
style: const TextStyle(fontSize: 14, color: Colors.black54),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Text(
"${AppLocalizations.of(context).toAccountNumber}: $creditAccount",
style: const TextStyle(
fontSize: 12,
color: Colors.black87,
),
style: const TextStyle(fontSize: 12, color: Colors.black87),
textAlign: TextAlign.center,
),
],
@@ -102,13 +90,13 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
icon: const Icon(Icons.share, size: 18),
label: Text(AppLocalizations.of(context).share),
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.white,
foregroundColor: Colors.blueAccent,
side:
const BorderSide(color: Colors.black, width: 1),
elevation: 0),
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.white,
foregroundColor: Colors.blueAccent,
side: const BorderSide(color: Colors.black, width: 1),
elevation: 0,
),
),
),
const SizedBox(width: 12),
@@ -117,10 +105,11 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
onPressed: () {
// Done action
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const NavigationScaffold()));
context,
MaterialPageRoute(
builder: (context) => const NavigationScaffold(),
),
);
},
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),