diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ffd4b2a..296743f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + + + + + + + + + + diff --git a/assets/images/avatar.jpg b/assets/images/avatar.jpg new file mode 100644 index 0000000..c3c98e9 Binary files /dev/null and b/assets/images/avatar.jpg differ diff --git a/assets/images/kccb_logo.svg b/assets/images/kccb_logo.svg new file mode 100644 index 0000000..28e81fc --- /dev/null +++ b/assets/images/kccb_logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index b8699b3..7024b20 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -45,5 +45,7 @@ UIApplicationSupportsIndirectInputEvents + NSFaceIDUsageDescription + We use Face ID to secure your data. diff --git a/lib/api/services/auth_service.dart b/lib/api/services/auth_service.dart index 6cbefc5..417c0f3 100644 --- a/lib/api/services/auth_service.dart +++ b/lib/api/services/auth_service.dart @@ -1,14 +1,20 @@ import 'dart:developer'; import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import '../../app.dart'; import '../../features/auth/models/auth_token.dart'; import '../../features/auth/models/auth_credentials.dart'; import '../../data/models/user.dart'; import '../../core/errors/exceptions.dart'; +import 'package:local_auth/local_auth.dart'; + +import '../../features/dashboard/screens/dashboard_screen.dart'; class AuthService { final Dio _dio; - AuthService(this._dio); Future login(AuthCredentials credentials) async { @@ -76,4 +82,39 @@ class AuthService { throw AuthException('Error fetching user profile: ${e.toString()}'); } } + + static Future authenticateWithBiometrics(BuildContext context) async { + final LocalAuthentication localAuth = LocalAuthentication(); + try { + bool isBiometricAvailable = await localAuth.canCheckBiometrics; + bool isAuthenticated = false; + + if (isBiometricAvailable) { + isAuthenticated = await localAuth.authenticate( + localizedReason: 'Touch the fingerprint sensor', + options: const AuthenticationOptions( + biometricOnly: true, + stickyAuth: true, + ), + ); + } + + if (isAuthenticated) { + // Navigate to Dashboard + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const NavigationScaffold()), + ); + } else { + // Show error/snack bar + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Authentication failed")), + ); + } + } catch (e) { + if (kDebugMode) { + print("Biometric error: $e"); + } + } + } } \ No newline at end of file diff --git a/lib/app.dart b/lib/app.dart index 6a2d1ca..ac567a4 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,13 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:kmobile/features/auth/screens/customer_info_screen.dart'; import 'config/themes.dart'; import 'config/routes.dart'; import 'di/injection.dart'; import 'features/auth/controllers/auth_cubit.dart'; import 'features/auth/controllers/auth_state.dart'; +import 'features/auth/screens/Card_screen.dart'; import 'features/auth/screens/login_screen.dart'; +import 'features/auth/screens/service_screen.dart'; import 'features/dashboard/screens/dashboard_screen.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; class KMobile extends StatelessWidget { const KMobile({super.key}); @@ -53,7 +57,7 @@ class KMobile extends StatelessWidget { } if (state is Authenticated) { - return const DashboardScreen(); + return const NavigationScaffold(); } return const LoginScreen(); @@ -104,3 +108,64 @@ class _SplashScreen extends StatelessWidget { ); } } + +class NavigationScaffold extends StatefulWidget { + const NavigationScaffold({super.key}); + + @override + State createState() => _NavigationScaffoldState(); +} + +class _NavigationScaffoldState extends State { + final PageController _pageController = PageController(); + int _selectedIndex = 0; + + final List _pages = [ + DashboardScreen(), + CardScreen(), + ServiceScreen(), + CustomerInfoScreen() + ]; + + void _onItemTapped(int index) { + setState(() { + _selectedIndex = index; + }); + _pageController.jumpToPage(index); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + children: _pages, + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _selectedIndex, + type: BottomNavigationBarType.fixed, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home_filled), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Symbols.credit_card), + label: 'Card', + ), + BottomNavigationBarItem( + icon: Icon(Symbols.concierge), + label: 'Services', + ), + ], + onTap: _onItemTapped, + backgroundColor: const Color(0xFFE0F7FA), // Light blue background + selectedItemColor: Colors.blue[800], + unselectedItemColor: Colors.black54, + ), + ); + } +} + + diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 76a34ae..e7795f7 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:kmobile/features/auth/screens/customer_info_screen.dart'; import 'package:kmobile/features/auth/screens/mpin_screen.dart'; +import '../app.dart'; import '../features/auth/screens/login_screen.dart'; // import '../features/auth/screens/forgot_password_screen.dart'; // import '../features/auth/screens/register_screen.dart'; @@ -19,10 +21,12 @@ class AppRoutes { static const String mPin = '/mPin'; static const String register = '/register'; static const String forgotPassword = '/forgot-password'; + static const String navigationScaffold = '/navigation-scaffold'; static const String dashboard = '/dashboard'; static const String accounts = '/accounts'; static const String transactions = '/transactions'; static const String payments = '/payments'; + static const String customer_info = '/customer-info'; // Route generator static Route generateRoute(RouteSettings settings) { @@ -42,9 +46,15 @@ class AppRoutes { // Placeholder - create the ForgotPasswordScreen class and uncomment // return MaterialPageRoute(builder: (_) => const ForgotPasswordScreen()); return _errorRoute(); + + case navigationScaffold: + return MaterialPageRoute(builder: (_) => const NavigationScaffold()); case dashboard: return MaterialPageRoute(builder: (_) => const DashboardScreen()); + + case customer_info: + return MaterialPageRoute(builder: (_) => const CustomerInfoScreen()); case accounts: // Placeholder - create the AccountsScreen class and uncomment diff --git a/lib/features/auth/screens/Card_screen.dart b/lib/features/auth/screens/Card_screen.dart new file mode 100644 index 0000000..68111a6 --- /dev/null +++ b/lib/features/auth/screens/Card_screen.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class CardScreen extends StatefulWidget { + const CardScreen({super.key}); + + @override + State createState() => _CardScreen(); +} + +class _CardScreen extends State{ + @override + Widget build(BuildContext context) { + return Scaffold( + + ); + } +} + diff --git a/lib/features/auth/screens/customer_info_screen.dart b/lib/features/auth/screens/customer_info_screen.dart new file mode 100644 index 0000000..5e6510d --- /dev/null +++ b/lib/features/auth/screens/customer_info_screen.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +class CustomerInfoScreen extends StatefulWidget { + const CustomerInfoScreen({super.key}); + + @override + State createState() => _CustomerInfoScreen(); +} + +class _CustomerInfoScreen extends State{ + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton(icon: const Icon(Symbols.arrow_back_ios_new), + onPressed: () { + Navigator.pop(context); + },), + title: const Text('kMobile', style: TextStyle(color: Colors.black, + fontWeight: FontWeight.w500),), + actions: const [ + Padding( + padding: EdgeInsets.only(right: 10.0), + child: CircleAvatar( + backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image + radius: 20, + ), + ), + ], + ), + + body: const SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Padding( + padding: EdgeInsets.all(16.0), + child: SafeArea( + child: Center( + child: Column( + children: [ + SizedBox(height: 30), + CircleAvatar( + backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image + radius: 50, + ), + + Padding( + padding: EdgeInsets.only(top: 10.0), + child: Text('Trina Bakshi', style: TextStyle(fontSize: 20, + color: Colors.black, fontWeight: FontWeight.w500),), + ), + + Text('CIF: 2553677487774', style: TextStyle(fontSize: 16, color: Colors.grey),), + SizedBox(height: 30,), + InfoField(label: 'Number of Active Accounts', value: '3'), + InfoField(label: 'Mobile Number', value: '987XXXXX78'), + InfoField(label: 'Date of Birth', value: '12-07-1984'), + InfoField(label: 'Branch', value: 'Krishnapur'), + InfoField(label: 'Aadhar Number', value: '7665 XXXX 1276'), + InfoField(label: 'PAN Number', value: '700127638009871'), + ], + ), + ), + ), + )), + ); + } +} + +class InfoField extends StatelessWidget { + final String label; + final String value; + + const InfoField({Key? key, required this.label, required this.value}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + const SizedBox(height: 3), + Text( + value, + style: const TextStyle( + fontSize: 16, + color: Colors.black, + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/auth/screens/login_screen.dart b/lib/features/auth/screens/login_screen.dart index 6c44a97..ed28161 100644 --- a/lib/features/auth/screens/login_screen.dart +++ b/lib/features/auth/screens/login_screen.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:kmobile/features/auth/screens/mpin_screen.dart'; +import '../../../app.dart'; import '../controllers/auth_cubit.dart'; import '../controllers/auth_state.dart'; import '../../dashboard/screens/dashboard_screen.dart'; @@ -46,7 +48,7 @@ class LoginScreenState extends State { listener: (context, state) { if (state is Authenticated) { Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (context) => const DashboardScreen()), + MaterialPageRoute(builder: (context) => const NavigationScaffold()), ); } else if (state is AuthError) { ScaffoldMessenger.of(context).showSnackBar( @@ -64,7 +66,7 @@ class LoginScreenState extends State { // crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Bank logo or app branding - const FlutterLogo(size: 100), + SvgPicture.asset('assets/images/kccb_logo.svg', width: 100, height: 100,), const SizedBox(height: 16), // Title const Text( diff --git a/lib/features/auth/screens/mpin_screen.dart b/lib/features/auth/screens/mpin_screen.dart index ed68e00..38dd703 100644 --- a/lib/features/auth/screens/mpin_screen.dart +++ b/lib/features/auth/screens/mpin_screen.dart @@ -1,4 +1,11 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import '../../../app.dart'; +import '../../dashboard/screens/dashboard_screen.dart'; +import '../../../api/services/auth_service.dart'; +import 'package:material_symbols_icons/get.dart'; class MPinScreen extends StatefulWidget { const MPinScreen({super.key}); @@ -48,7 +55,7 @@ class MPinScreenState extends State { ['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9'], - ['', '0', '<'] + ['Enter', '0', '<'] ]; return Column( @@ -62,6 +69,17 @@ class MPinScreenState extends State { onTap: () { if (key == '<') { deleteDigit(); + } else if (key == 'Enter') { + if (mPin.length == 4) { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const NavigationScaffold()), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Please enter 4 digits")), + ); + } } else if (key.isNotEmpty) { addDigit(key); } @@ -74,9 +92,14 @@ class MPinScreenState extends State { color: Colors.grey[200], ), alignment: Alignment.center, - child: Text( + child: key == 'Enter' ? const Icon(Symbols.check) : Text( key == '<' ? '⌫' : key, - style: const TextStyle(fontSize: 24), + style: TextStyle( + fontSize: 20, + fontWeight: key == 'Enter' ? + FontWeight.normal : FontWeight.normal, + color: key == 'Enter' ? Colors.blue : Colors.black, + ), ), ), ), @@ -107,7 +130,9 @@ class MPinScreenState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - TextButton(onPressed: () {}, child: const Text("Try another way")), + TextButton(onPressed: () { + AuthService.authenticateWithBiometrics(context); + }, child: const Text("Try another way")), TextButton(onPressed: () {}, child: const Text("Register?")), ], ), diff --git a/lib/features/auth/screens/service_screen.dart b/lib/features/auth/screens/service_screen.dart new file mode 100644 index 0000000..b31b622 --- /dev/null +++ b/lib/features/auth/screens/service_screen.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class ServiceScreen extends StatefulWidget { + const ServiceScreen({super.key}); + + @override + State createState() => _ServiceScreen(); +} + +class _ServiceScreen extends State{ + @override + Widget build(BuildContext context) { + return Scaffold( + + ); + } +} \ No newline at end of file diff --git a/lib/features/dashboard/screens/dashboard_screen.dart b/lib/features/dashboard/screens/dashboard_screen.dart index 17b80ac..510bf5f 100644 --- a/lib/features/dashboard/screens/dashboard_screen.dart +++ b/lib/features/dashboard/screens/dashboard_screen.dart @@ -1,11 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../auth/controllers/auth_cubit.dart'; -import '../../auth/controllers/auth_state.dart'; -import '../widgets/account_card.dart'; -import '../widgets/transaction_list_item.dart'; -import '../../accounts/models/account.dart'; -import '../../transactions/models/transaction.dart'; +import 'package:kmobile/features/auth/screens/customer_info_screen.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @@ -15,333 +10,139 @@ class DashboardScreen extends StatefulWidget { } class _DashboardScreenState extends State { - // Mock data for demonstration - final List _accounts = [ - Account( - id: '1', - accountNumber: '**** 4589', - accountType: 'Savings', - balance: 12450.75, - currency: 'USD', - ), - Account( - id: '2', - accountNumber: '**** 7823', - accountType: 'Checking', - balance: 3840.50, - currency: 'USD', - ), - ]; - - final List _recentTransactions = [ - Transaction( - id: 't1', - description: 'Coffee Shop', - amount: -4.50, - date: DateTime.now().subtract(const Duration(hours: 3)), - category: 'Food & Drink', - ), - Transaction( - id: 't2', - description: 'Salary Deposit', - amount: 2500.00, - date: DateTime.now().subtract(const Duration(days: 2)), - category: 'Income', - ), - Transaction( - id: 't3', - description: 'Electric Bill', - amount: -85.75, - date: DateTime.now().subtract(const Duration(days: 3)), - category: 'Utilities', - ), - Transaction( - id: 't4', - description: 'Amazon Purchase', - amount: -32.50, - date: DateTime.now().subtract(const Duration(days: 5)), - category: 'Shopping', - ), + // Mock data for transactions + final List> transactions = [ + {'name': 'Raj Kumar', 'amount': '₹1,000', 'date': '09 March, 2025 16:04', 'type': 'in'}, + {'name': 'Sunita Joshi', 'amount': '₹1,45,000', 'date': '07 March, 2025 16:04', 'type': 'out'}, + {'name': 'Manoj Singh', 'amount': '₹2,400', 'date': '07 March, 2025 16:04', 'type': 'in'}, + {'name': 'Raj Kumar', 'amount': '₹11,500', 'date': '09 March, 2025 16:04', 'type': 'in'}, + {'name': 'Manoj Singh', 'amount': '₹1,000', 'date': '', 'type': 'in'}, ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Dashboard'), + automaticallyImplyLeading: false, + title: const Text('kMobile', style: TextStyle(color: Colors.blueAccent, + fontWeight: FontWeight.w500),), actions: [ - IconButton( - icon: const Icon(Icons.notifications_outlined), - onPressed: () { - // Navigate to notifications - }, - ), - IconButton( - icon: const Icon(Icons.logout), - onPressed: () { - _showLogoutConfirmation(context); - }, + // IconButton( + // icon: const Icon(Icons.notifications_outlined), + // onPressed: () { + // // Navigate to notifications + // }, + // ), + Padding( + padding: const EdgeInsets.only(right: 10.0), + child: InkWell( + borderRadius: BorderRadius.circular(20), + onTap: (){ + Navigator.push(context, MaterialPageRoute( + builder: (context) => const CustomerInfoScreen())); + }, + child: const CircleAvatar( + backgroundImage: AssetImage('assets/images/avatar.jpg'), // Replace with your image + radius: 20, + ), + ), ), ], ), - body: RefreshIndicator( - onRefresh: () async { - // Implement refresh logic to fetch updated data - await Future.delayed(const Duration(seconds: 1)); - }, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Greeting section - BlocBuilder( - builder: (context, state) { - if (state is Authenticated) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Hello, ${state.user.username}', - style: Theme.of(context).textTheme.headlineSmall, - ), - const SizedBox(height: 4), - Text( - 'Welcome back to your banking app', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Colors.grey[600], - ), - ), - ], - ); - } - return Container(); - }, + body: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + + // Account Info Card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.blue[700], + borderRadius: BorderRadius.circular(16), ), - - const SizedBox(height: 24), - - // Account cards section - const Text( - 'Your Accounts', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - SizedBox( - height: 180, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: _accounts.length + 1, // +1 for the "Add Account" card - itemBuilder: (context, index) { - if (index < _accounts.length) { - return Padding( - padding: const EdgeInsets.only(right: 16.0), - child: AccountCard(account: _accounts[index]), - ); - } else { - // "Add Account" card - return Container( - width: 300, - margin: const EdgeInsets.only(right: 16.0), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.grey[300]!), - ), - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.add_circle_outline, - size: 40, - color: Theme.of(context).primaryColor, - ), - const SizedBox(height: 8), - Text( - 'Add New Account', - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ); - } - }, - ), - ), - - const SizedBox(height: 32), - - // Quick Actions section - const Text( - 'Quick Actions', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildQuickActionButton( - icon: Icons.swap_horiz, - label: 'Transfer', - onTap: () { - // Navigate to transfer screen - }, + Row( + children: [ + Text("Account Number: ", style: + TextStyle(color: Colors.white, fontSize: 12)), + Text("03000156789462302", style: + TextStyle(color: Colors.white, fontSize: 14)), + Padding( + padding: EdgeInsets.only(left: 5.0), + child: Icon(Symbols.keyboard_arrow_down, color: Colors.white), + ), + ], ), - _buildQuickActionButton( - icon: Icons.payment, - label: 'Pay Bills', - onTap: () { - // Navigate to bill payment screen - }, - ), - _buildQuickActionButton( - icon: Icons.qr_code_scanner, - label: 'Scan & Pay', - onTap: () { - // Navigate to QR code scanner - }, - ), - _buildQuickActionButton( - icon: Icons.more_horiz, - label: 'More', - onTap: () { - // Show more options - }, + SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("₹ *****", style: TextStyle(color: Colors.white, + fontSize: 24, fontWeight: FontWeight.w700)), + Icon(Symbols.visibility_lock, color: Colors.white), + ], ), + SizedBox(height: 20), ], ), - - const SizedBox(height: 32), - - // Recent Transactions section - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - 'Recent Transactions', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - TextButton( - onPressed: () { - // Navigate to all transactions - }, - child: const Text('See All'), - ), - ], - ), - const SizedBox(height: 8), - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: _recentTransactions.length, - itemBuilder: (context, index) { - return TransactionListItem( - transaction: _recentTransactions[index], - ); - }, - ), - ], - ), + ), + const SizedBox(height: 18), + const Text('Quick Links', style: TextStyle(fontSize: 15),), + const SizedBox(height: 16), + + // Quick Links + GridView.count( + crossAxisCount: 4, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + _buildQuickLink(Symbols.id_card, "Customer \n Info"), + _buildQuickLink(Symbols.currency_rupee, "Quick \n Pay"), + _buildQuickLink(Symbols.send_money, "Fund \n Transfer"), + _buildQuickLink(Symbols.server_person, "Account \n Info"), + _buildQuickLink(Symbols.receipt_long, "Account \n History"), + _buildQuickLink(Symbols.checkbook, "Handle \n Cheque"), + _buildQuickLink(Icons.group, "Manage \n Beneficiary"), + _buildQuickLink(Symbols.support_agent, "Contact \n Us"), + ], + ), + const SizedBox(height: 10), + + // Recent Transactions + const Align( + alignment: Alignment.centerLeft, + child: Text("Recent Transactions", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + ), + ...transactions.map((tx) => ListTile( + leading: Icon(tx['type'] == 'in' ? Symbols.call_received : Symbols.call_made, color: tx['type'] == 'in' ? Colors.green : Colors.red), + title: Text(tx['name']!), + subtitle: Text(tx['date']!), + trailing: Text(tx['amount']!), + )), + + + ], ), ), ), - bottomNavigationBar: BottomNavigationBar( - currentIndex: 0, - type: BottomNavigationBarType.fixed, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.dashboard), - label: 'Home', - ), - BottomNavigationBarItem( - icon: Icon(Icons.account_balance_wallet), - label: 'Accounts', - ), - BottomNavigationBarItem( - icon: Icon(Icons.show_chart), - label: 'Insights', - ), - BottomNavigationBarItem( - icon: Icon(Icons.person), - label: 'Profile', - ), - ], - onTap: (index) { - // Handle navigation - }, - ), ); } - Widget _buildQuickActionButton({ - required IconData icon, - required String label, - required VoidCallback onTap, - }) { - return GestureDetector( - onTap: onTap, - child: Column( - children: [ - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Theme.of(context).primaryColor.withAlpha((0.1 * 255).toInt()), - borderRadius: BorderRadius.circular(12), - ), - child: Icon( - icon, - color: Theme.of(context).primaryColor, - size: 28, - ), - ), - const SizedBox(height: 8), - Text( - label, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ); - } - - void _showLogoutConfirmation(BuildContext context) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Logout'), - content: const Text('Are you sure you want to logout?'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('CANCEL'), - ), - TextButton( - onPressed: () { - Navigator.pop(context); - context.read().logout(); - }, - child: const Text('LOGOUT'), - ), - ], - ), + Widget _buildQuickLink(IconData icon, String label) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 30, color: Colors.blue[700]), + const SizedBox(height: 4), + Text(label, textAlign: TextAlign.center, style: const TextStyle(fontSize: 12)), + ], ); } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index cdf8472..0418b7d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -25,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + chalkdart: + dependency: transitive + description: + name: chalkdart + sha256: "7ffc6bd39c81453fb9ba8dbce042a9c960219b75ea1c07196a7fa41c2fab9e86" + url: "https://pub.dev" + source: hosted + version: "3.0.5" characters: dependency: transitive description: @@ -97,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" flutter: dependency: "direct main" description: flutter @@ -118,6 +142,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.dev" + source: hosted + version: "2.0.28" flutter_secure_storage: dependency: "direct main" description: @@ -166,6 +198,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 + url: "https://pub.dev" + source: hosted + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -184,6 +224,22 @@ packages: url: "https://pub.dev" source: hosted version: "8.0.3" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + http: + dependency: transitive + description: + name: http + sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f + url: "https://pub.dev" + source: hosted + version: "1.3.0" http_parser: dependency: transitive description: @@ -240,6 +296,46 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + local_auth: + dependency: "direct main" + description: + name: local_auth + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "63ad7ca6396290626dc0cb34725a939e4cfe965d80d36112f08d49cf13a8136e" + url: "https://pub.dev" + source: hosted + version: "1.0.49" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2" + url: "https://pub.dev" + source: hosted + version: "1.4.3" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" matcher: dependency: transitive description: @@ -256,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.11.1" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: d45b6c36c3effa8cb51b1afb8698107d5ff1f88fa4631428f34a8a01abc295d7 + url: "https://pub.dev" + source: hosted + version: "4.2815.0" meta: dependency: transitive description: @@ -280,6 +384,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" path_provider: dependency: transitive description: @@ -328,6 +440,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.dev" + source: hosted + version: "6.1.0" platform: dependency: transitive description: @@ -413,6 +533,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" + url: "https://pub.dev" + source: hosted + version: "1.1.18" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" + url: "https://pub.dev" + source: hosted + version: "1.1.16" vector_math: dependency: transitive description: @@ -453,6 +597,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" sdks: dart: ">=3.7.0 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 1515047..549d9a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,9 @@ dependencies: flutter_bloc: ^9.1.0 get_it: ^8.0.3 intl: any + flutter_svg: ^2.1.0 + local_auth: ^2.3.0 + material_symbols_icons: ^4.2815.0 dev_dependencies: flutter_test: @@ -66,9 +69,9 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/images/kccb_logo.svg + - assets/images/avatar.jpg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware