implemented TPIN and quick pay within bank

This commit is contained in:
2025-06-23 04:47:05 +05:30
parent 0d2dfc817e
commit 77a2654401
44 changed files with 1692 additions and 1153 deletions

View File

@@ -1,6 +1,9 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:kmobile/data/repositories/transaction_repository.dart';
import 'package:kmobile/di/injection.dart';
import 'package:kmobile/features/accounts/screens/account_info_screen.dart';
import 'package:kmobile/features/accounts/screens/account_statement_screen.dart';
@@ -8,7 +11,6 @@ import 'package:kmobile/features/auth/controllers/auth_cubit.dart';
import 'package:kmobile/features/auth/controllers/auth_state.dart';
import 'package:kmobile/features/customer_info/screens/customer_info_screen.dart';
import 'package:kmobile/features/beneficiaries/screens/manage_beneficiaries_screen.dart';
import 'package:kmobile/features/dashboard/widgets/transaction_list_placeholder.dart';
import 'package:kmobile/features/enquiry/screens/enquiry_screen.dart';
import 'package:kmobile/features/fund_transfer/screens/fund_transfer_beneficiary_screen.dart';
import 'package:kmobile/features/quick_pay/screens/quick_pay_screen.dart';
@@ -17,6 +19,7 @@ import 'package:local_auth/local_auth.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shimmer/shimmer.dart';
import 'package:kmobile/data/models/transaction.dart';
class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
@@ -31,6 +34,34 @@ class _DashboardScreenState extends State<DashboardScreen> {
bool isRefreshing = false;
bool isBalanceLoading = false;
bool _biometricPromptShown = false;
bool _txLoading = false;
List<Transaction> _transactions = [];
bool _txInitialized = false;
Future<void> _loadTransactions(String accountNo) async {
setState(() {
_txLoading = true;
_transactions = [];
});
try {
final repo = getIt<TransactionRepository>();
final txs = await repo.fetchTransactions(accountNo);
var fiveTxns = <Transaction>[];
//only take the first 5 transactions
if (txs.length > 5) {
fiveTxns = txs.sublist(0, 5);
} else {
fiveTxns = txs;
}
setState(() => _transactions = fiveTxns);
} catch (e) {
log(accountNo, error: e);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to load transactions: $e')));
} finally {
setState(() => _txLoading = false);
}
}
Future<void> _refreshAccountData(BuildContext context) async {
setState(() {
@@ -200,6 +231,13 @@ class _DashboardScreenState extends State<DashboardScreen> {
if (state is Authenticated) {
final users = state.users;
final currAccount = users[selectedAccountIndex];
// firsttime load
if (!_txInitialized) {
_txInitialized = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
_loadTransactions(currAccount.accountNo!);
});
}
final firstName = getProcessedFirstName(currAccount.name);
return SingleChildScrollView(
@@ -267,7 +305,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
selectedAccountIndex = newIndex;
});
await Future.delayed(
const Duration(seconds: 1));
const Duration(milliseconds: 200));
setState(() {
isBalanceLoading = false;
});
@@ -276,6 +314,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
selectedAccountIndex = newIndex;
});
}
await _loadTransactions(
users[newIndex].accountNo!);
},
),
const Spacer(),
@@ -381,8 +421,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const QuickPayScreen()));
builder: (context) => QuickPayScreen(
debitAccount: currAccount.accountNo!)));
}),
_buildQuickLink(Symbols.send_money, "Fund \n Transfer",
() {
@@ -391,7 +431,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
MaterialPageRoute(
builder: (context) =>
const FundTransferBeneficiaryScreen()));
}),
}, disable: true),
_buildQuickLink(
Symbols.server_person, "Account \n Info", () {
Navigator.push(
@@ -405,8 +445,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const AccountStatementScreen()));
builder: (context) => AccountStatementScreen(
accountNo: users[selectedAccountIndex]
.accountNo!,
)));
}),
_buildQuickLink(
Symbols.checkbook, "Handle \n Cheque", () {},
@@ -418,7 +460,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
MaterialPageRoute(
builder: (context) =>
const ManageBeneficiariesScreen()));
}),
}, disable: true),
_buildQuickLink(Symbols.support_agent, "Contact \n Us",
() {
Navigator.push(
@@ -436,25 +478,43 @@ class _DashboardScreenState extends State<DashboardScreen> {
style: TextStyle(fontSize: 17),
),
const SizedBox(height: 16),
if (currAccount.transactions != null &&
currAccount.transactions!.isNotEmpty)
...currAccount.transactions!.map((tx) => ListTile(
if (_txLoading)
..._buildTransactionShimmer()
else if (_transactions.isNotEmpty)
..._transactions.map((tx) => ListTile(
leading: Icon(
tx.type == 'CR'
? Symbols.call_received
: Symbols.call_made,
color: tx.type == 'CR'
? Colors.green
: Colors.red),
title: Text(tx.name ?? ''),
subtitle: Text(tx.date ?? ''),
tx.type == 'CR'
? Symbols.call_received
: Symbols.call_made,
color:
tx.type == 'CR' ? Colors.green : Colors.red,
),
title: Text(
tx.name != null
? (tx.name!.length > 18
? tx.name!.substring(0, 20)
: tx.name!)
: '',
style: const TextStyle(fontSize: 14),
),
subtitle: Text(tx.date ?? '',
style: const TextStyle(fontSize: 12)),
trailing: Text("${tx.amount}",
style: const TextStyle(
fontSize: 16,
)),
style: const TextStyle(fontSize: 16)),
))
else
const EmptyTransactionsPlaceholder(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Center(
child: Text(
'No transactions found for this account.',
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
),
),
],
),
),
@@ -466,6 +526,28 @@ class _DashboardScreenState extends State<DashboardScreen> {
);
}
List<Widget> _buildTransactionShimmer() {
return List.generate(3, (i) {
return ListTile(
leading: Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: const CircleAvatar(radius: 12, backgroundColor: Colors.white),
),
title: Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Container(height: 10, width: 100, color: Colors.white),
),
subtitle: Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Container(height: 8, width: 60, color: Colors.white),
),
);
});
}
Widget _buildQuickLink(IconData icon, String label, VoidCallback onTap,
{bool disable = false}) {
return InkWell(