16 Commits

Author SHA1 Message Date
3024ddef15 Manage Beneficiary Services 2 2025-08-10 11:18:00 +05:30
83609fb778 Manage Beneficiary Services 2025-08-09 22:10:26 +05:30
dbc61abf00 Manage Beneficiary UI 2025-08-08 23:37:16 +05:30
763c101f58 Manage Beneficiary 2025-08-08 15:43:08 +05:30
117e2d5786 Manage Beneficiary fetch 2025-08-08 08:40:38 +05:30
ae40f61c01 Add Beneficiary Animations and Localizations 2025-08-07 23:36:28 +05:30
a1365b19d5 Add Beneficiary Animations 2025-08-07 21:07:32 +05:30
2dd7f4079b Add Beneficiary Validation API integration 2025-08-07 20:32:04 +05:30
99e23bf21d IFSC Code Validation and Add Beneficiary Validation API integration 2025-08-07 18:12:31 +05:30
c4d4261afc Theme 2025-08-06 17:26:25 +05:30
Nilanjan Chakrabarti
2fdef7c850 Shared Preferences Update 2025-07-31 16:14:29 +05:30
Nilanjan Chakrabarti
618bd4a9b9 Shared Preferences implementation 2025-07-30 12:50:11 +05:30
ee3961215a Merge branch 'language-translation' of https://git.7o9o.net/md.asif5/kmobile into language-translation 2025-07-24 22:24:30 +05:30
787fcdc2e2 changed imports of AppLocalization
Also added beneficiary validation while quick pay within bank
2025-07-24 22:21:19 +05:30
Nilanjan Chakrabarti
20791432dd Welcome Screen for both Login and Mpin Page 2025-07-18 12:48:52 +05:30
Nilanjan Chakrabarti
23d742ace9 New Update with Account Number Dropdown in Account Info 2025-07-11 12:09:38 +05:30
54 changed files with 5570 additions and 1849 deletions

View File

@@ -3,7 +3,7 @@ template-arb-file: app_en.arb
output-localization-file: app_localizations.dart output-localization-file: app_localizations.dart
output-class: AppLocalizations output-class: AppLocalizations
use-deferred-loading: false use-deferred-loading: false
synthetic-package: false
use-maybe-of: true use-maybe-of: true
synthetic-package: true
nullable-getter : false nullable-getter : false
untranslated-message-file: l10n_missing_translations.json untranslated-message-file: l10n_missing_translations.json

View File

@@ -33,7 +33,14 @@ class AuthInterceptor extends Interceptor {
ErrorInterceptorHandler handler, ErrorInterceptorHandler handler,
) async { ) async {
// Handle 401 errors by refreshing token and retrying // Handle 401 errors by refreshing token and retrying
if (err.response?.statusCode == 401) { final response = err.response;
if (response?.statusCode == 401) {
final data = response?.data;
// Only refresh token if error is NOT INCORRECT_TPIN (or similar business error)
if (data is Map && data['error'] == 'INCORRECT_TPIN') {
// Pass the error through, do not retry
return handler.next(err);
}
// On 401, try to get a new token // On 401, try to get a new token
final token = await _authRepository.getAccessToken(); final token = await _authRepository.getAccessToken();

View File

@@ -0,0 +1,179 @@
import 'package:dio/dio.dart';
import 'package:kmobile/data/models/ifsc.dart';
import 'package:kmobile/data/models/beneficiary.dart';
class BeneficiaryService {
final Dio _dio;
BeneficiaryService(this._dio);
Future<String> validateBeneficiaryWithinBank(String accountNumber) async {
try {
final response = await _dio.get('/api/beneficiary/validate/within-bank', queryParameters: {
'accountNumber': accountNumber,
});
if (response.statusCode == 200) {
return response.data['name'];
} else {
throw Exception(response.data['error'] ?? 'Failed to validate beneficiary');
}
} on DioException catch (e) {
throw Exception('Network error: ${e.message}');
} catch (e) {
throw Exception('Unexpected error: ${e.toString()}');
}
}
Future<ifsc?> validateIFSC(String ifscCode) async {
try {
final response = await _dio.get('/api/beneficiary/ifsc-details', queryParameters: {
"ifscCode": ifscCode
}
);
if (response.statusCode == 200) {
return ifsc.fromJson(response.data);
}
} on DioException catch (e) {
if (e.response?.statusCode == 404) {
print('Invalid IFSC code.');
} else {
print('API error: ${e.message}');
}
} catch (e) {
print('Unexpected error: $e');
}
return null;
}
///Step 1: Validate beneficiary (returns refNo)
Future<String?> validateBeneficiary({
required String accountNo,
required String ifscCode,
required String remitterName,
}) async {
try {
final response = await _dio.post(
'/api/beneficiary/validate/outside_bank',
queryParameters: {
'accountNo': accountNo,
'ifscCode': ifscCode,
'remitterName': remitterName,
},
);
if (response.statusCode == 200) {
return response.data['refNo'];
} else {
return null;
}
} catch (e) {
print("Error validating beneficiary: $e");
return null;
}
}
/// Step 2: Check validation status (returns Beneficiary name if success)
Future<String?> checkValidationStatus(String refNo) async {
try {
final response = await _dio.get(
'/api/beneficiary/check',
queryParameters: {
'refNo': refNo,
},
);
if (response.statusCode == 200 && response.data != null) {
return Beneficiary.fromJson(response.data).name;
} else {
return null;
}
} catch (e) {
print("Error checking validation status: $e");
return null;
}
}
// Send Data for Validation
Future<void> sendForValidation(Beneficiary beneficiary) async {
try {
print(beneficiary.toJson());
final response = await _dio.post(
'/api/beneficiary/add',
data: beneficiary.toJson(),
);
if (response.statusCode == 200) {
print("SENT FOR VALIDATION");
} else {
print("VALIDATION REQUEST FAILED: ${response.statusCode}");
}
} catch (e) {
print("ERROR IN SENDING REQUEST: $e");
rethrow;
}
}
// Poll to check if beneficiary is found
Future<bool> checkIfFound(String accountNo) async {
const int timeoutInSeconds = 30;
const int intervalInSeconds = 2;
const int maxTries = timeoutInSeconds ~/ intervalInSeconds;
int attempts = 0;
while (attempts < maxTries) {
try {
final response = await _dio.get(
'/api/beneficiary/check?',
queryParameters: {
'accountNo': accountNo
}
);
if (response.statusCode == 200) {
print("FOUND");
return true;
} else if (response.statusCode == 404) {
print("NOT FOUND");
}
} catch (e) {
print("ERROR DURING STATUS: $e");
}
attempts++;
}
print("Beneficiary not found within timeout.");
return false;
}
Future<List<Beneficiary>> fetchBeneficiaryList() async{
try {
final response = await _dio.get(
"/api/beneficiary/get",
options: Options(
headers: {
"Content-Type": "application/json",
},
),
);
if (response.statusCode == 200) {
return Beneficiary.listFromJson(response.data);
} else {
throw Exception("Failed to fetch beneficiaries");
}
} catch (e) {
print("Error fetching beneficiaries: $e");
return [];
}
}
}

View File

@@ -11,7 +11,7 @@ class PaymentService {
Future<PaymentResponse> processQuickPayWithinBank(Transfer transfer) async { Future<PaymentResponse> processQuickPayWithinBank(Transfer transfer) async {
try { try {
await Future.delayed(const Duration(seconds: 3)); // Simulate delay await Future.delayed(const Duration(seconds: 3)); // Simulate delay
final response =
await _dio.post('/api/payment/transfer', data: transfer.toJson()); await _dio.post('/api/payment/transfer', data: transfer.toJson());
return PaymentResponse( return PaymentResponse(
@@ -20,8 +20,6 @@ class PaymentService {
creditedAccount: transfer.toAccount, creditedAccount: transfer.toAccount,
amount: transfer.amount, amount: transfer.amount,
currency: "INR", currency: "INR",
errorMessage: response.data['errorMessage'],
errorCode: response.data['errorCode'],
); );
} on DioException catch (e) { } on DioException catch (e) {
log('DioException: ${e.toString()}'); log('DioException: ${e.toString()}');

View File

@@ -4,8 +4,9 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kmobile/security/secure_storage.dart'; import 'package:kmobile/security/secure_storage.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import './l10n/app_localizations.dart';
import 'config/themes.dart'; import 'package:kmobile/features/auth/controllers/theme_cubit.dart';
import 'package:kmobile/features/auth/controllers/theme_state.dart';
import 'config/routes.dart'; import 'config/routes.dart';
import 'di/injection.dart'; import 'di/injection.dart';
import 'features/auth/controllers/auth_cubit.dart'; import 'features/auth/controllers/auth_cubit.dart';
@@ -14,9 +15,9 @@ import 'features/auth/screens/welcome_screen.dart';
import 'features/auth/screens/login_screen.dart'; import 'features/auth/screens/login_screen.dart';
import 'features/service/screens/service_screen.dart'; import 'features/service/screens/service_screen.dart';
import 'features/dashboard/screens/dashboard_screen.dart'; import 'features/dashboard/screens/dashboard_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'features/auth/screens/mpin_screen.dart'; import 'features/auth/screens/mpin_screen.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
import 'package:shared_preferences/shared_preferences.dart';
class KMobile extends StatefulWidget { class KMobile extends StatefulWidget {
const KMobile({super.key}); const KMobile({super.key});
@@ -25,27 +26,39 @@ class KMobile extends StatefulWidget {
State<KMobile> createState() => _KMobileState(); State<KMobile> createState() => _KMobileState();
static void setLocale(BuildContext context, Locale newLocale) { static void setLocale(BuildContext context, Locale newLocale) {
final _KMobileState? state = final _KMobileState? state = context.findAncestorStateOfType<_KMobileState>();
context.findAncestorStateOfType<_KMobileState>();
state?.setLocale(newLocale); state?.setLocale(newLocale);
} }
} }
class _KMobileState extends State<KMobile> { class _KMobileState extends State<KMobile> {
bool _showSplash = false; bool showSplash = true;
Locale? _locale;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Simulate a splash screen delay loadPreferences();
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
setState(() { setState(() {
_showSplash = false; showSplash = false;
}); });
}); });
} }
Locale? _locale; Future<void> loadPreferences() async {
final prefs = await SharedPreferences.getInstance();
// Load Locale
final String? langCode = prefs.getString('locale');
if (langCode != null) {
setState(() {
_locale = Locale(langCode);
});
}
}
void setLocale(Locale locale) { void setLocale(Locale locale) {
setState(() { setState(() {
@@ -53,94 +66,54 @@ class _KMobileState extends State<KMobile> {
}); });
} }
/*
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Set status bar color // Set status bar color and brightness
SystemChrome.setSystemUIOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle( const SystemUiOverlayStyle(
statusBarColor: Colors.transparent, statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark, statusBarIconBrightness: Brightness.dark,
), ),
); );
if (_showSplash) { return MultiBlocProvider(
return MaterialApp( providers: [
debugShowCheckedModeBanner: false, BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()),
locale: _locale, BlocProvider<ThemeCubit>(create: (_) => getIt<ThemeCubit>()),
supportedLocales: const [ ],
Locale('en'), child: BlocBuilder<ThemeCubit, ThemeState>(
Locale('hi'), builder: (context, themeState) {
], print('global theme state changed');
localizationsDelegates: const [ print(themeState);
AppLocalizations.delegate, return MaterialApp(
GlobalMaterialLocalizations.delegate, debugShowCheckedModeBanner: false,
GlobalWidgetsLocalizations.delegate, locale: _locale ?? const Locale('en'),
GlobalCupertinoLocalizations.delegate, supportedLocales: const [
], Locale('en'),
home: const SplashScreen(), Locale('hi'),
); ],
} localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
return MultiBlocProvider( GlobalCupertinoLocalizations.delegate,
providers: [ ],
BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()), title: 'kMobile',
], //theme: AppThemes.getLightTheme(themeState.themeType),
child: MaterialApp( theme: themeState.getThemeData(),
title: 'kMobile', // darkTheme: AppThemes.getDarkTheme(themeState.themeType),
// debugShowCheckedModeBanner: false, themeMode: ThemeMode.system,
theme: AppThemes.lightTheme, onGenerateRoute: AppRoutes.generateRoute,
// darkTheme: AppThemes.darkTheme, initialRoute: AppRoutes.splash,
themeMode: ThemeMode.system, // Use system theme by default home: showSplash ? const SplashScreen() : const AuthGate(),
onGenerateRoute: AppRoutes.generateRoute, );
initialRoute: AppRoutes.splash, },
home: const AuthGate(), ),
), );
);
}
*/
@override
Widget build(BuildContext context) {
// Set status bar color
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
);
return MultiBlocProvider(
providers: [
BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
locale: _locale, // Use your existing locale variable
supportedLocales: const [
Locale('en'),
Locale('hi'),
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
title: 'kMobile',
theme: AppThemes.lightTheme,
// darkTheme: AppThemes.darkTheme,
themeMode: ThemeMode.system, // Use system theme by default
onGenerateRoute: AppRoutes.generateRoute,
initialRoute: AppRoutes.splash,
home: _showSplash ? const SplashScreen() : const AuthGate(),
),
);
}
} }
}
class AuthGate extends StatefulWidget { class AuthGate extends StatefulWidget {
const AuthGate({super.key}); const AuthGate({super.key});
@@ -169,7 +142,8 @@ class _AuthGateState extends State<AuthGate> {
final mpin = await storage.read('mpin'); final mpin = await storage.read('mpin');
final biometric = await storage.read('biometric_enabled'); final biometric = await storage.read('biometric_enabled');
setState(() { setState(() {
_isLoggedIn = accessToken != null && _isLoggedIn =
accessToken != null &&
accessTokenExpiry != null && accessTokenExpiry != null &&
DateTime.parse(accessTokenExpiry).isAfter(DateTime.now()); DateTime.parse(accessTokenExpiry).isAfter(DateTime.now());
_hasMPin = mpin != null; _hasMPin = mpin != null;
@@ -198,112 +172,24 @@ class _AuthGateState extends State<AuthGate> {
} }
} }
/* @override
Widget build(BuildContext context) {
if (_checking) {
return const SplashScreen();
}
if (_isLoggedIn) {
if (_hasMPin) {
if (_biometricEnabled) {
return FutureBuilder<bool>(
future: _tryBiometric(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SplashScreen();
}
if (snapshot.data == true) {
// Authenticated with biometrics, go to dashboard
return const NavigationScaffold();
}
// If not authenticated or user dismissed, show mPIN screen
return MPinScreen(
mode: MPinMode.enter,
onCompleted: (_) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) => const NavigationScaffold()),
);
},
);
},
);
} else {
return MPinScreen(
mode: MPinMode.enter,
onCompleted: (_) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const NavigationScaffold()),
);
},
);
}
} else {
return MPinScreen(
mode: MPinMode.set,
onCompleted: (_) async {
final storage = getIt<SecureStorage>();
final localAuth = LocalAuthentication();
// 1) Prompt user to optin for biometric
final optIn = await showDialog<bool>(
context: context,
barrierDismissible: false, // force choice
builder: (ctx) => AlertDialog(
title: const Text('Enable Fingerprint Login?'),
content: const Text(
'Would you like to enable fingerprint authentication for faster login?',
),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
child: const Text('No'),
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: const Text('Yes'),
),
],
),
);
// 2) If opted in, perform biometric auth
if (optIn == true) {
final canCheck = await localAuth.canCheckBiometrics;
bool didAuth = false;
if (canCheck) {
didAuth = await localAuth.authenticate(
localizedReason: 'Authenticate to enable fingerprint login',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true,
),
);
}
await storage.write(
'biometric_enabled', didAuth ? 'true' : 'false');
} else {
await storage.write('biometric_enabled', 'false');
}
// 3) Finally go to your main scaffold
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const NavigationScaffold()),
);
},
);
}
}
return const LoginScreen();
}
}*/
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_checking) { if (_checking) {
return const SplashScreen(); return const SplashScreen();
} }
// ✅ Step 1: Show welcome screen first, only once
if (_showWelcome) {
return WelcomeScreen(
onContinue: () {
setState(() {
_showWelcome = false;
});
},
);
}
// ✅ Step 2: Check login status
if (_isLoggedIn) { if (_isLoggedIn) {
if (_hasMPin) { if (_hasMPin) {
if (_biometricEnabled) { if (_biometricEnabled) {
@@ -318,7 +204,7 @@ class _AuthGateState extends State<AuthGate> {
return const NavigationScaffold(); // Authenticated return const NavigationScaffold(); // Authenticated
} }
// Failed or dismissed biometric → Show MPIN // ❌ Biometric failed → Show MPIN screen
return MPinScreen( return MPinScreen(
mode: MPinMode.enter, mode: MPinMode.enter,
onCompleted: (_) { onCompleted: (_) {
@@ -344,20 +230,21 @@ class _AuthGateState extends State<AuthGate> {
); );
} }
} else { } else {
// No MPIN set → show MPIN set screen + biometric dialog
return MPinScreen( return MPinScreen(
mode: MPinMode.set, mode: MPinMode.set,
onCompleted: (_) async { onCompleted: (_) async {
final storage = getIt<SecureStorage>(); final storage = getIt<SecureStorage>();
final localAuth = LocalAuthentication(); final localAuth = LocalAuthentication();
final optin = await showDialog<bool>(
final optIn = await showDialog<bool>(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
title: title:
Text(AppLocalizations.of(context).enableFingerprintLogin), Text(AppLocalizations.of(context).enableFingerprintLogin),
content: Text( content:
AppLocalizations.of(context).enableFingerprintMessage, Text(AppLocalizations.of(context).enableFingerprintMessage),
),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.of(ctx).pop(false), onPressed: () => Navigator.of(ctx).pop(false),
@@ -371,7 +258,7 @@ class _AuthGateState extends State<AuthGate> {
), ),
); );
if (optin == true) { if (optIn == true) {
final canCheck = await localAuth.canCheckBiometrics; final canCheck = await localAuth.canCheckBiometrics;
bool didAuth = false; bool didAuth = false;
@@ -384,15 +271,12 @@ class _AuthGateState extends State<AuthGate> {
biometricOnly: true, biometricOnly: true,
), ),
); );
}
await storage.write( await storage.write(
'biometric_enabled', 'biometric_enabled', didAuth ? 'true' : 'false');
didAuth ? 'true' : 'false',
);
} else { } else {
await storage.write('biometric_enabled', 'false'); await storage.write('biometric_enabled', 'false');
} }
}
Navigator.of(context).pushReplacement( Navigator.of(context).pushReplacement(
MaterialPageRoute( MaterialPageRoute(
@@ -404,16 +288,7 @@ class _AuthGateState extends State<AuthGate> {
} }
} }
// 🔻 Show Welcome screen before login if not logged in // Step 3: If not logged in, show login screen
if (_showWelcome) {
return WelcomeScreen(
onContinue: () {
setState(() {
_showWelcome = false;
});
},
);
}
return const LoginScreen(); return const LoginScreen();
} }
} }
@@ -482,8 +357,8 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex, currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
backgroundColor: const Color(0xFFE0F7FA), // Light blue background backgroundColor: Theme.of(context).scaffoldBackgroundColor, // Light blue background
selectedItemColor: Colors.blue[800], selectedItemColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.black54, unselectedItemColor: Colors.black54,
onTap: _onItemTapped, onTap: _onItemTapped,
items: [ items: [
@@ -499,7 +374,8 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
icon: const Icon(Icons.miscellaneous_services), icon: const Icon(Icons.miscellaneous_services),
label: AppLocalizations.of(context).services, label: AppLocalizations.of(context).services,
), ),
]), ],
),
), ),
); );
} }
@@ -517,8 +393,10 @@ class SplashScreen extends StatelessWidget {
children: [ children: [
const CircularProgressIndicator(), const CircularProgressIndicator(),
const SizedBox(height: 20), const SizedBox(height: 20),
Text(AppLocalizations.of(context).loading, Text(
style: Theme.of(context).textTheme.headlineMedium), AppLocalizations.of(context).loading,
style: Theme.of(context).textTheme.headlineMedium,
),
], ],
), ),
), ),

View File

@@ -0,0 +1,6 @@
enum ThemeType {
violet,
green,
orange,
blue,
}

View File

@@ -1,266 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'theme_type.dart';
class AppThemes { class AppThemes {
// Private constructor to prevent instantiation static ThemeData getLightTheme(ThemeType type) {
AppThemes._(); switch (type) {
case ThemeType.green:
return ThemeData(primarySwatch: Colors.green);
case ThemeType.orange:
return ThemeData(primarySwatch: Colors.orange);
case ThemeType.blue:
return ThemeData(primarySwatch: Colors.blue);
case ThemeType.violet:
default:
return ThemeData(primarySwatch: Colors.deepPurple);
}
}
// Light theme colors static ThemeData getDarkTheme(ThemeType type) {
static const Color _primaryColorLight = Color(0xFF1E88E5); // Blue 600 switch (type) {
static const Color _secondaryColorLight = Color(0xFF26A69A); // Teal 400 case ThemeType.green:
static const Color _errorColorLight = Color(0xFFE53935); // Red 600 return ThemeData.dark().copyWith(primaryColor: Colors.green);
static const Color _surfaceColorLight = Colors.white; case ThemeType.orange:
return ThemeData.dark().copyWith(primaryColor: Colors.orange);
// Dark theme colors case ThemeType.blue:
static const Color _primaryColorDark = Color(0xFF42A5F5); // Blue 400 return ThemeData.dark().copyWith(primaryColor: Colors.blue);
static const Color _secondaryColorDark = Color(0xFF4DB6AC); // Teal 300 case ThemeType.violet:
static const Color _errorColorDark = Color(0xFFEF5350); // Red 400 default:
static const Color _surfaceColorDark = Color(0xFF1E1E1E); return ThemeData.dark().copyWith(primaryColor: Colors.deepPurple);
}
// Text themes }
static const TextTheme _textThemeLight = TextTheme(
displayLarge: TextStyle(
fontSize: 96,
fontWeight: FontWeight.w300,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
displayMedium: TextStyle(
fontSize: 60,
fontWeight: FontWeight.w300,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
displaySmall: TextStyle(
fontSize: 48,
fontWeight: FontWeight.w400,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
headlineMedium: TextStyle(
fontSize: 34,
fontWeight: FontWeight.w400,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
headlineSmall: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w400,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
titleLarge: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
bodyLarge: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
bodyMedium: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
bodySmall: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xFF757575),
fontFamily: 'Rubik',
),
labelLarge: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF212121),
fontFamily: 'Rubik',
),
);
static final TextTheme _textThemeDark = _textThemeLight.copyWith(
displayLarge: _textThemeLight.displayLarge?.copyWith(color: Colors.white),
displayMedium: _textThemeLight.displayMedium?.copyWith(color: Colors.white),
displaySmall: _textThemeLight.displaySmall?.copyWith(color: Colors.white),
headlineMedium:
_textThemeLight.headlineMedium?.copyWith(color: Colors.white),
headlineSmall: _textThemeLight.headlineSmall?.copyWith(color: Colors.white),
titleLarge: _textThemeLight.titleLarge?.copyWith(color: Colors.white),
bodyLarge: _textThemeLight.bodyLarge?.copyWith(color: Colors.white),
bodyMedium: _textThemeLight.bodyMedium?.copyWith(color: Colors.white),
bodySmall: _textThemeLight.bodySmall?.copyWith(color: Colors.white70),
labelLarge: _textThemeLight.labelLarge?.copyWith(color: Colors.white),
);
// Light theme
static final ThemeData lightTheme = ThemeData(
useMaterial3: true,
fontFamily: 'Rubik',
colorScheme: const ColorScheme.light(
primary: _primaryColorLight,
secondary: _secondaryColorLight,
error: _errorColorLight,
surface: _surfaceColorLight,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: Colors.black87,
onError: Colors.white,
brightness: Brightness.light,
),
textTheme: _textThemeLight,
appBarTheme: const AppBarTheme(
elevation: 0,
backgroundColor: _surfaceColorLight,
foregroundColor: Color(0xFF212121),
centerTitle: true,
),
cardTheme: CardTheme(
//Earlier CardThemeData
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: _primaryColorLight,
elevation: 2,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: _primaryColorLight,
side: const BorderSide(color: _primaryColorLight),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: _primaryColorLight,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.grey[100],
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.grey[300]!),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: _primaryColorLight, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: _errorColorLight, width: 2),
),
labelStyle: const TextStyle(color: Colors.grey),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
selectedItemColor: _primaryColorLight,
unselectedItemColor: Colors.grey,
),
);
// Dark theme
static final ThemeData darkTheme = ThemeData(
fontFamily: 'Rubik',
useMaterial3: true,
colorScheme: const ColorScheme.dark(
primary: _primaryColorDark,
secondary: _secondaryColorDark,
error: _errorColorDark,
surface: _surfaceColorDark,
onPrimary: Colors.black,
onSecondary: Colors.black,
onSurface: Colors.white,
onError: Colors.black,
brightness: Brightness.dark,
),
textTheme: _textThemeDark,
appBarTheme: const AppBarTheme(
elevation: 0,
backgroundColor: _surfaceColorDark,
foregroundColor: Colors.white,
centerTitle: true,
),
cardTheme: CardTheme(
//Earlier was CardThemeData
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: const Color(0xFF2C2C2C),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: _primaryColorDark,
elevation: 2,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: _primaryColorDark,
side: const BorderSide(color: _primaryColorDark),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: _primaryColorDark,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: const Color(0xFF2A2A2A),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFF3A3A3A)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: _primaryColorDark, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: _errorColorDark, width: 2),
),
labelStyle: const TextStyle(color: Colors.grey),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
selectedItemColor: _primaryColorDark,
unselectedItemColor: Colors.grey,
backgroundColor: _surfaceColorDark,
),
);
} }

View File

@@ -0,0 +1,52 @@
// ignore_for_file: non_constant_identifier_names
class Beneficiary {
final String accountNo;
final String accountType;
final String name;
final String ifscCode;
final String? bankName;
final String? branchName;
Beneficiary({
required this.accountNo,
required this.accountType,
required this.name,
required this.ifscCode,
this.bankName,
this.branchName,
});
factory Beneficiary.fromJson(Map<String, dynamic> json) {
return Beneficiary(
accountNo: json['accountNo'] ?? '',
accountType: json['accountType'] ?? '',
name: json['name'] ?? '',
ifscCode: json['ifscCode'] ?? '',
bankName: json['bankName'] ?? '',
branchName: json['branchName'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'accountNo': accountNo,
'accountType': accountType,
'name': name,
'ifscCode' : ifscCode,
};
}
static List<Beneficiary> listFromJson(List<dynamic> jsonList) {
final beneficiaryList = jsonList.map((beneficiary) => Beneficiary.fromJson(beneficiary)).toList();
print(beneficiaryList);
return beneficiaryList;
}
@override
String toString() {
return 'Beneficiary(accountNo: $accountNo, accountType: $accountType, ifscCode: $ifscCode, name: $name)';
}
}

View File

@@ -0,0 +1,45 @@
class BeneficiaryRecieve {
final String accountNo;
final String accountType;
final String name;
final String ifscCode;
final String bankName;
final String branchName;
BeneficiaryRecieve({
required this.accountNo,
required this.accountType,
required this.name,
required this.ifscCode,
required this.bankName,
required this.branchName,
});
factory BeneficiaryRecieve.fromJson(Map<String, dynamic> json) {
return BeneficiaryRecieve(
accountNo: json['account_no'] ?? '',
accountType: json['account_type'] ?? '',
name: json['name'] ?? '',
ifscCode: json['ifsc_code'] ?? '',
bankName: json['bank_name'] ?? '',
branchName: json['branch_name'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'account_no': accountNo,
'account_type': accountType,
'name': name,
'ifsc_code' : ifscCode,
'bank_name' : bankName,
'branch_name' : branchName
};
}
@override
String toString() {
return 'ListBeneficiary(accountNo: $accountNo, accountType: $accountType, ifscCode: $ifscCode, name: $name, bankName: $bankName, branchName: $branchName)';
}
}

32
lib/data/models/ifsc.dart Normal file
View File

@@ -0,0 +1,32 @@
class ifsc {
final String ifscCode;
final String bankName;
final String branchName;
ifsc({
required this.ifscCode,
required this.bankName,
required this.branchName,
});
factory ifsc.fromJson(Map<String, dynamic> json) {
return ifsc(
ifscCode: json['ifsc_code'] ?? '',
bankName: json['bank_name'] ?? '',
branchName: json['branch_name'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'ifsc_code': ifscCode,
'bank_name': bankName,
'branch_name': branchName,
};
}
@override
String toString() {
return 'IFSC(ifscCode: $ifscCode, bankName: $bankName, branchName: $branchName)';
}
}

View File

@@ -1,17 +1,25 @@
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:kmobile/api/services/beneficiary_service.dart';
import 'package:kmobile/api/services/payment_service.dart'; import 'package:kmobile/api/services/payment_service.dart';
import 'package:kmobile/api/services/user_service.dart'; import 'package:kmobile/api/services/user_service.dart';
import 'package:kmobile/data/repositories/transaction_repository.dart'; import 'package:kmobile/data/repositories/transaction_repository.dart';
import 'package:kmobile/features/auth/controllers/theme_cubit.dart';
import '../api/services/auth_service.dart'; import '../api/services/auth_service.dart';
import '../api/interceptors/auth_interceptor.dart'; import '../api/interceptors/auth_interceptor.dart';
import '../data/repositories/auth_repository.dart'; import '../data/repositories/auth_repository.dart';
import '../features/auth/controllers/auth_cubit.dart'; import '../features/auth/controllers/auth_cubit.dart';
import '../security/secure_storage.dart'; import '../security/secure_storage.dart';
final getIt = GetIt.instance; final getIt = GetIt.instance;
Future<void> setupDependencies() async { Future<void> setupDependencies() async {
//getIt.registerLazySingleton<ThemeController>(() => ThemeController());
//getIt.registerLazySingleton<ThemeModeController>(() => ThemeModeController());
getIt.registerSingleton<ThemeCubit>( ThemeCubit());
// Register Dio client // Register Dio client
getIt.registerSingleton<Dio>(_createDioClient()); getIt.registerSingleton<Dio>(_createDioClient());
@@ -33,6 +41,7 @@ Future<void> setupDependencies() async {
TransactionRepositoryImpl(getIt<Dio>())); TransactionRepositoryImpl(getIt<Dio>()));
getIt.registerSingleton<PaymentService>(PaymentService(getIt<Dio>())); getIt.registerSingleton<PaymentService>(PaymentService(getIt<Dio>()));
getIt.registerSingleton<BeneficiaryService>(BeneficiaryService(getIt<Dio>()));
// Add auth interceptor after repository is available // Add auth interceptor after repository is available
getIt<Dio>().interceptors.add( getIt<Dio>().interceptors.add(
@@ -42,13 +51,15 @@ Future<void> setupDependencies() async {
// Register controllers/cubits // Register controllers/cubits
getIt.registerFactory<AuthCubit>( getIt.registerFactory<AuthCubit>(
() => AuthCubit(getIt<AuthRepository>(), getIt<UserService>())); () => AuthCubit(getIt<AuthRepository>(), getIt<UserService>()));
} }
Dio _createDioClient() { Dio _createDioClient() {
final dio = Dio( final dio = Dio(
BaseOptions( BaseOptions(
baseUrl: baseUrl:
'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080', //'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080',
'http://localhost:8081',
connectTimeout: const Duration(seconds: 5), connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3), receiveTimeout: const Duration(seconds: 3),
headers: { headers: {

View File

@@ -2,21 +2,34 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/data/models/user.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 AccountInfoScreen extends StatefulWidget { class AccountInfoScreen extends StatefulWidget {
final User user; final List<User> users;
final int selectedIndex;
const AccountInfoScreen({super.key, required this.user}); const AccountInfoScreen({
super.key,
required this.users,
required this.selectedIndex,
});
@override @override
State<AccountInfoScreen> createState() => _AccountInfoScreen(); State<AccountInfoScreen> createState() => _AccountInfoScreen();
} }
class _AccountInfoScreen extends State<AccountInfoScreen> { class _AccountInfoScreen extends State<AccountInfoScreen> {
late User selectedUser;
@override
void initState() {
super.initState();
selectedUser = widget.users[widget.selectedIndex];
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final user = widget.user; final users = widget.users;
int selectedIndex = widget.selectedIndex;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: IconButton(
@@ -27,7 +40,10 @@ class _AccountInfoScreen extends State<AccountInfoScreen> {
), ),
title: Text( title: Text(
AppLocalizations.of(context).accountInfo, AppLocalizations.of(context).accountInfo,
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
),
), ),
centerTitle: false, centerTitle: false,
actions: [ actions: [
@@ -49,32 +65,62 @@ class _AccountInfoScreen extends State<AccountInfoScreen> {
body: ListView( body: ListView(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
children: [ children: [
InfoRow( Text(
AppLocalizations.of(context).accountNumber,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 14),
),
/// Dropdown to change account
DropdownButton<User>(
value: selectedUser,
onChanged: (User? newUser) {
if (newUser != null) {
setState(() {
selectedUser = newUser;
});
}
},
items: widget.users.map((user) {
return DropdownMenuItem<User>(
value: user,
child: Text(user.accountNo.toString()),
);
}).toList(),
),
/*InfoRow(
title: AppLocalizations.of(context).accountNumber, title: AppLocalizations.of(context).accountNumber,
value: user.accountNo ?? 'N/A'), value: users[selectedIndex].accountNo ?? 'N/A'),
// InfoRow(title: 'Nominee Customer No', value: user.nomineeCustomerNo), // InfoRow(title: 'Nominee Customer No', value: user.nomineeCustomerNo),
// InfoRow(title: 'SMS Service', value: user.smsService), // InfoRow(title: 'SMS Service', value: user.smsService),
// InfoRow(title: 'Missed Call Service', value: user.missedCallService), // InfoRow(title: 'Missed Call Service', value: user.missedCallService),*/
InfoRow( InfoRow(
title: AppLocalizations.of(context).customerNumber, title: AppLocalizations.of(context).customerNumber,
value: user.cifNumber ?? 'N/A'), value: selectedUser.cifNumber ?? 'N/A',
),
InfoRow( InfoRow(
title: AppLocalizations.of(context).productName, title: AppLocalizations.of(context).productName,
value: user.productType ?? 'N/A'), value: selectedUser.productType ?? 'N/A',
// InfoRow(title: 'Account Opening Date', value: user.accountOpeningDate ?? 'N/A'), ),
// InfoRow(title: 'Account Opening Date', value: users[selectedIndex].accountOpeningDate ?? 'N/A'),
InfoRow( InfoRow(
title: AppLocalizations.of(context).accountStatus, value: 'OPEN'), title: AppLocalizations.of(context).accountStatus,
value: 'OPEN',
),
InfoRow( InfoRow(
title: AppLocalizations.of(context).availableBalance, title: AppLocalizations.of(context).availableBalance,
value: user.availableBalance ?? 'N/A'), value: selectedUser.availableBalance ?? 'N/A',
),
InfoRow( InfoRow(
title: AppLocalizations.of(context).currentBalance, title: AppLocalizations.of(context).currentBalance,
value: user.currentBalance ?? 'N/A'), value: selectedUser.currentBalance ?? 'N/A',
),
user.approvedAmount != null users[selectedIndex].approvedAmount != null
? InfoRow( ? InfoRow(
title: AppLocalizations.of(context).approvedAmount, title: AppLocalizations.of(context).approvedAmount,
value: user.approvedAmount ?? 'N/A') value: selectedUser.approvedAmount ?? 'N/A',
)
: const SizedBox.shrink(), : const SizedBox.shrink(),
], ],
), ),
@@ -107,10 +153,7 @@ class InfoRow extends StatelessWidget {
const SizedBox(height: 3), const SizedBox(height: 3),
Text( Text(
value, value,
style: const TextStyle( style: const TextStyle(fontSize: 16, color: Colors.black),
fontSize: 16,
color: Colors.black,
),
), ),
], ],
), ),

View File

@@ -5,14 +5,11 @@ import 'package:shimmer/shimmer.dart';
import 'package:kmobile/data/models/transaction.dart'; import 'package:kmobile/data/models/transaction.dart';
import 'package:kmobile/data/repositories/transaction_repository.dart'; import 'package:kmobile/data/repositories/transaction_repository.dart';
import 'package:kmobile/di/injection.dart'; import 'package:kmobile/di/injection.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
class AccountStatementScreen extends StatefulWidget { class AccountStatementScreen extends StatefulWidget {
final String accountNo; final String accountNo;
const AccountStatementScreen({ const AccountStatementScreen({super.key, required this.accountNo});
super.key,
required this.accountNo,
});
@override @override
State<AccountStatementScreen> createState() => _AccountStatementScreen(); State<AccountStatementScreen> createState() => _AccountStatementScreen();
@@ -46,7 +43,9 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
'${AppLocalizations.of(context).failedToLoadTransactions} $e')), '${AppLocalizations.of(context).failedToLoadTransactions} $e',
),
),
); );
} finally { } finally {
setState(() => _txLoading = false); setState(() => _txLoading = false);
@@ -73,7 +72,8 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
if (fromDate == null) { if (fromDate == null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context).pleaseSelectDateFirst)), content: Text(AppLocalizations.of(context).pleaseSelectDateFirst),
),
); );
return; return;
} }
@@ -115,7 +115,7 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
), ),
title: Text( title: Text(
AppLocalizations.of(context).accountStatement, AppLocalizations.of(context).accountStatement,
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
), ),
centerTitle: false, centerTitle: false,
actions: [ actions: [
@@ -139,8 +139,23 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(AppLocalizations.of(context).filters, Row(
style: TextStyle(fontSize: 17)), children: [
Text(
"${AppLocalizations.of(context).accountNumber}: ",
style: const TextStyle(
fontSize: 19,
fontWeight: FontWeight.bold,
),
),
Text(widget.accountNo, style: const TextStyle(fontSize: 17)),
],
),
const SizedBox(height: 15),
Text(
AppLocalizations.of(context).filters,
style: const TextStyle(fontSize: 17),
),
const SizedBox(height: 15), const SizedBox(height: 15),
Row( Row(
children: [ children: [
@@ -148,7 +163,9 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
child: GestureDetector( child: GestureDetector(
onTap: () => _selectFromDate(context), onTap: () => _selectFromDate(context),
child: buildDateBox( child: buildDateBox(
AppLocalizations.of(context).fromDate, fromDate), AppLocalizations.of(context).fromDate,
fromDate,
),
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
@@ -156,7 +173,9 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
child: GestureDetector( child: GestureDetector(
onTap: () => _selectToDate(context), onTap: () => _selectToDate(context),
child: buildDateBox( child: buildDateBox(
AppLocalizations.of(context).toDate, toDate), AppLocalizations.of(context).toDate,
toDate,
),
), ),
), ),
], ],
@@ -169,10 +188,10 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
controller: _minAmountController, controller: _minAmountController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).minAmount, labelText: AppLocalizations.of(context).minAmount,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
@@ -184,10 +203,10 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
controller: _maxAmountController, controller: _maxAmountController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).maxAmount, labelText: AppLocalizations.of(context).maxAmount,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
@@ -201,9 +220,9 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
), ),
child: const Icon( child: Icon(
Symbols.arrow_forward, Symbols.arrow_forward,
color: Colors.white, color: Theme.of(context).scaffoldBackgroundColor,
size: 30, size: 30,
), ),
), ),
@@ -231,20 +250,28 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
leading: Shimmer.fromColors( leading: Shimmer.fromColors(
baseColor: Colors.grey[300]!, baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!, highlightColor: Colors.grey[100]!,
child: const CircleAvatar( child: CircleAvatar(
radius: 12, backgroundColor: Colors.white), radius: 12,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
),
), ),
title: Shimmer.fromColors( title: Shimmer.fromColors(
baseColor: Colors.grey[300]!, baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!, highlightColor: Colors.grey[100]!,
child: Container( child: Container(
height: 10, width: 100, color: Colors.white), height: 10,
width: 100,
color: Theme.of(context).scaffoldBackgroundColor,
),
), ),
subtitle: Shimmer.fromColors( subtitle: Shimmer.fromColors(
baseColor: Colors.grey[300]!, baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!, highlightColor: Colors.grey[100]!,
child: Container( child: Container(
height: 8, width: 60, color: Colors.white), height: 8,
width: 60,
color: Theme.of(context).scaffoldBackgroundColor,
),
), ),
), ),
) )
@@ -253,44 +280,37 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
child: Text( child: Text(
AppLocalizations.of(context).noTransactions, AppLocalizations.of(context).noTransactions,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16, color: Colors.grey[600]),
color: Colors.grey[600],
),
), ),
) )
: ListView.builder( : ListView.builder(
itemCount: _transactions.length, itemCount: _transactions.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final txn = _transactions[index]; final tx = _transactions[index];
final isCredit = txn.type == 'CR'; return ListTile(
return Column( leading: Icon(
crossAxisAlignment: CrossAxisAlignment.start, tx.type == 'CR'
children: [ ? Symbols.call_received
Text(txn.date ?? '', : Symbols.call_made,
style: const TextStyle(color: Colors.grey)), color:
const SizedBox(height: 4), tx.type == 'CR' ? Colors.green : Colors.red,
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(txn.name ?? '',
style: const TextStyle(fontSize: 16)),
), ),
Text( title: Text(
"${isCredit ? '+' : '-'}${txn.amount}", tx.name != null
style: TextStyle( ? (tx.name!.length > 18
color: isCredit ? tx.name!.substring(0, 20)
? Colors.green : tx.name!)
: Colors.red, : '',
// fontWeight: FontWeight.bold, style: const TextStyle(fontSize: 14),
fontSize: 16,
), ),
subtitle: Text(
tx.date ?? '',
style: const TextStyle(fontSize: 12),
), ),
], trailing: Text(
"${tx.amount}",
style: const TextStyle(fontSize: 16),
), ),
const SizedBox(height: 25),
],
); );
}, },
), ),

View File

@@ -0,0 +1,48 @@
import 'dart:developer';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'theme_state.dart';
import 'package:kmobile/config/theme_type.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ThemeCubit extends Cubit<ThemeState> {
ThemeCubit(): super(ThemeViolet()) {
loadTheme();
}
Future<void> loadTheme() async {
final prefs = await SharedPreferences.getInstance();
final themeIndex = prefs.getInt('theme_type') ?? 0;
// final isDark = prefs.getBool('is_dark_mode') ?? false;
final type = ThemeType.values[themeIndex];
switch(type) {
case ThemeType.blue:
emit(ThemeBlue());
case ThemeType.violet:
emit(ThemeViolet());
default:
emit(ThemeViolet());
}
}
Future<void> changeTheme(ThemeType type) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('theme_type', type.index);
log("Mode Change");
print("mode changed");
switch(type) {
case ThemeType.blue:
emit(ThemeBlue());
print('blue matched');
break;
case ThemeType.violet:
emit(ThemeViolet());
print('violet matched');
break;
default:
emit(ThemeBlue());
print('default macthed');
}
}
}

View File

@@ -0,0 +1,27 @@
import 'package:equatable/equatable.dart';
import 'package:kmobile/config/theme_type.dart';
import 'package:kmobile/config/themes.dart';
abstract class ThemeState extends Equatable {
getThemeData();
@override
List<Object?> get props => [];
}
class ThemeBlue extends ThemeState {
@override
getThemeData() {
print('returning blue theme');
return AppThemes.getLightTheme(ThemeType.blue);
}
}
class ThemeViolet extends ThemeState {
@override
getThemeData() {
print('returning violet theme');
return AppThemes.getLightTheme(ThemeType.violet);
}
}

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:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -69,10 +69,13 @@ class LoginScreenState extends State<LoginScreen>
builder: (_) => MPinScreen( builder: (_) => MPinScreen(
mode: MPinMode.set, mode: MPinMode.set,
onCompleted: (_) { onCompleted: (_) {
Navigator.of(context, rootNavigator: true) Navigator.of(
.pushReplacement( context,
rootNavigator: true,
).pushReplacement(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => const NavigationScaffold()), builder: (_) => const NavigationScaffold(),
),
); );
}, },
), ),
@@ -84,9 +87,9 @@ class LoginScreenState extends State<LoginScreen>
); );
} }
} else if (state is AuthError) { } else if (state is AuthError) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text(state.message)), context,
); ).showSnackBar(SnackBar(content: Text(state.message)));
} }
}, },
builder: (context, state) { builder: (context, state) {
@@ -97,7 +100,7 @@ class LoginScreenState extends State<LoginScreen>
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// 🔁 Animated Blinking Logo // 🔁 Animated Blinking Logo
FadeTransition( FadeTransition(
opacity: _logoAnimation, opacity: _logoAnimation,
child: Image.asset( child: Image.asset(
@@ -105,8 +108,11 @@ class LoginScreenState extends State<LoginScreen>
width: 150, width: 150,
height: 150, height: 150,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.account_balance, return Icon(
size: 100, color: Colors.blue); Icons.account_balance,
size: 100,
color: Theme.of(context).primaryColor,
);
}, },
), ),
), ),
@@ -117,7 +123,8 @@ class LoginScreenState extends State<LoginScreen>
style: TextStyle( style: TextStyle(
fontSize: 32, fontSize: 32,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.blue), color: Theme.of(context).primaryColor,
),
), ),
const SizedBox(height: 48), const SizedBox(height: 48),
@@ -129,7 +136,7 @@ class LoginScreenState extends State<LoginScreen>
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -147,7 +154,7 @@ class LoginScreenState extends State<LoginScreen>
}, },
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
// Password // Password
TextFormField( TextFormField(
controller: _passwordController, controller: _passwordController,
obscureText: _obscurePassword, obscureText: _obscurePassword,
@@ -159,7 +166,7 @@ class LoginScreenState extends State<LoginScreen>
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: const OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -195,8 +202,8 @@ class LoginScreenState extends State<LoginScreen>
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.white, backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.blueAccent, foregroundColor: Theme.of(context).primaryColorDark,
side: const BorderSide(color: Colors.black, width: 1), side: const BorderSide(color: Colors.black, width: 1),
elevation: 0, elevation: 0,
), ),
@@ -235,8 +242,9 @@ class LoginScreenState extends State<LoginScreen>
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.lightBlue[100], backgroundColor: Theme.of(context).primaryColorLight,
foregroundColor: Colors.black), foregroundColor: Colors.black,
),
child: Text(AppLocalizations.of(context).register), child: Text(AppLocalizations.of(context).register),
), ),
), ),
@@ -250,7 +258,6 @@ class LoginScreenState extends State<LoginScreen>
} }
} }
/*import 'package:flutter/material.dart'; /*import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kmobile/di/injection.dart'; import 'package:kmobile/di/injection.dart';

View File

@@ -1,4 +1,4 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import 'dart:developer'; import 'dart:developer';
@@ -155,7 +155,7 @@ class _MPinScreenState extends State<MPinScreen> {
['1', '2', '3'], ['1', '2', '3'],
['4', '5', '6'], ['4', '5', '6'],
['7', '8', '9'], ['7', '8', '9'],
['Enter', '0', '<'] ['Enter', '0', '<'],
]; ];
return Column( return Column(
@@ -174,8 +174,9 @@ class _MPinScreenState extends State<MPinScreen> {
_handleComplete(); _handleComplete();
} else { } else {
setState(() { setState(() {
errorText = errorText = AppLocalizations.of(
AppLocalizations.of(context).pleaseEnter4Digits; context,
).pleaseEnter4Digits;
}); });
} }
} else if (key.isNotEmpty) { } else if (key.isNotEmpty) {
@@ -196,7 +197,7 @@ class _MPinScreenState extends State<MPinScreen> {
key == '<' ? '' : key, key == '<' ? '' : key,
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
color: key == 'Enter' ? Colors.blue : Colors.black, color: key == 'Enter' ? Theme.of(context).primaryColor : Colors.black,
), ),
), ),
), ),
@@ -238,8 +239,10 @@ class _MPinScreenState extends State<MPinScreen> {
if (errorText != null) if (errorText != null)
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: child: Text(
Text(errorText!, style: const TextStyle(color: Colors.red)), errorText!,
style: const TextStyle(color: Colors.red),
),
), ),
const Spacer(), const Spacer(),
buildNumberPad(), 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:flutter/material.dart';
import 'dart:async'; import 'dart:async';
@@ -17,10 +17,13 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
void initState() { void initState() {
super.initState(); super.initState();
// Automatically go to login after 6 seconds // Automatically go to logizn after 4 seconds
Timer(const Duration(seconds: 6), () { Timer(const Duration(seconds: 4), () {
widget.onContinue(); widget.onContinue();
}); }
);
} }
@override @override
@@ -46,17 +49,17 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
style: TextStyle( style: TextStyle(
fontSize: 36, fontSize: 36,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white, color: Theme.of(context).dialogBackgroundColor,
letterSpacing: 1.5, letterSpacing: 1.5,
), ),
), ),
SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
AppLocalizations.of(context).kccBankFull, AppLocalizations.of(context).kccBankFull,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
color: Colors.white, color: Theme.of(context).dialogBackgroundColor,
letterSpacing: 1.2, letterSpacing: 1.2,
), ),
), ),
@@ -65,14 +68,12 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
), ),
/// 🔹 Loading Spinner at Bottom /// 🔹 Loading Spinner at Bottom
const Positioned( Positioned(
bottom: 40, bottom: 40,
left: 0, left: 0,
right: 0, right: 0,
child: Center( child: Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(color: Theme.of(context).scaffoldBackgroundColor),
color: Colors.white,
),
), ),
), ),
], ],
@@ -138,6 +139,3 @@ class WelcomeScreen extends StatelessWidget {
} }
} }
*/ */

View File

@@ -1,10 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:kmobile/api/services/beneficiary_service.dart';
import 'package:kmobile/data/models/ifsc.dart';
import 'package:kmobile/data/models/beneficiary.dart';
import 'package:kmobile/data/models/user.dart';
import 'beneficiary_result_page.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../di/injection.dart';
import '../../../l10n/app_localizations.dart';
class AddBeneficiaryScreen extends StatefulWidget { class AddBeneficiaryScreen extends StatefulWidget {
const AddBeneficiaryScreen({super.key}); final List<User>? users;
final int? selectedIndex;
const AddBeneficiaryScreen({
super.key,
this.users,
this.selectedIndex,
});
@override @override
State<AddBeneficiaryScreen> createState() => _AddBeneficiaryScreen(); State<AddBeneficiaryScreen> createState() => _AddBeneficiaryScreen();
@@ -13,6 +26,7 @@ class AddBeneficiaryScreen extends StatefulWidget {
class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> { class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
late User selectedUser = (widget.users ?? [])[widget.selectedIndex!];
final TextEditingController accountNumberController = TextEditingController(); final TextEditingController accountNumberController = TextEditingController();
final TextEditingController confirmAccountNumberController = final TextEditingController confirmAccountNumberController =
TextEditingController(); TextEditingController();
@@ -22,6 +36,11 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
final TextEditingController ifscController = TextEditingController(); final TextEditingController ifscController = TextEditingController();
final TextEditingController phoneController = TextEditingController(); final TextEditingController phoneController = TextEditingController();
String? _beneficiaryName;
bool _isValidating = false;
bool _isBeneficiaryValidated = false;
String? _validationError;
late String accountType; late String accountType;
@override @override
@@ -34,95 +53,167 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
}); });
} }
void _submitForm() {
if (_formKey.currentState!.validate()) {
// Handle successful submission ifsc? _ifscData;
ScaffoldMessenger.of(context).showSnackBar( bool _isLoading = false; //for validateIFSC()
SnackBar(
backgroundColor: Colors.grey[900],
behavior: SnackBarBehavior.floating,
margin: const EdgeInsets.all(12),
duration: const Duration(seconds: 5),
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
AppLocalizations.of(context).beneficiaryAdded,
style: TextStyle(color: Colors.white),
),
),
TextButton(
onPressed: () {
// Navigate to Payment Screen or do something
},
style: TextButton.styleFrom(
foregroundColor: Colors.blue[200],
),
child: Text(AppLocalizations.of(context).payNow),
),
IconButton(
icon: const Icon(Icons.close, color: Colors.white),
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
],
),
),
);
}
}
void _validateIFSC() async { void _validateIFSC() async {
var beneficiaryService = getIt<BeneficiaryService>();
final ifsc = ifscController.text.trim().toUpperCase(); final ifsc = ifscController.text.trim().toUpperCase();
if (ifsc.isEmpty) return;
setState(() {
_isLoading = true;
_ifscData = null;
});
// 🔹 Format check final result = await beneficiaryService.validateIFSC(ifsc);
final isValidFormat = RegExp(r'^[A-Z]{4}0[A-Z0-9]{6}$').hasMatch(ifsc);
if (!isValidFormat) { setState(() {
_isLoading = false;
_ifscData = result;
});
if (result == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context).invalidIfsc)),
);
bankNameController.clear(); bankNameController.clear();
branchNameController.clear(); branchNameController.clear();
ScaffoldMessenger.of(context).showSnackBar( } else {
SnackBar(content: Text(AppLocalizations.of(context).invalidIfscFormat)), print("${AppLocalizations.of(context).validIfsc}: ${result.bankName}, ${result.branchName}");
bankNameController.text = result.bankName;
branchNameController.text = result.branchName;
}
}
Future<void> _validateBeneficiary() async {
// start spinner / disable button
setState(() {
_isValidating = true;
_validationError = null;
_isBeneficiaryValidated = false;
nameController.text = ''; // clear previous name
});
final String accountNo = accountNumberController.text.trim();
final String ifsc = ifscController.text.trim();
final String remitter = selectedUser.name ?? '';
final service = getIt<BeneficiaryService>();
try {
// Step 1: call validate API -> get refNo
final String? refNo = await service.validateBeneficiary(
accountNo: accountNo,
ifscCode: ifsc,
remitterName: remitter,
); );
if (refNo == null || refNo.isEmpty) {
setState(() {
_validationError = 'Validation request failed. Please check details.';
_isBeneficiaryValidated = false;
});
return; return;
} }
await Future.delayed(
const Duration(seconds: 2)); //Mock delay for 2 sec to imitate api call
// 🔹 Mock IFSC lookup (you can replace this with API)
const mockIfscData = {
'KCCB0001234': {
'bank': 'Kangra Central Co-operative Bank',
'branch': 'Dharamshala',
},
'SBIN0004567': {
'bank': 'State Bank of India',
'branch': 'Connaught Place',
},
};
if (mockIfscData.containsKey(ifsc)) { // Step 2: poll checkValidationStatus for up to 30 seconds
final data = mockIfscData[ifsc]!; const int timeoutSeconds = 30;
bankNameController.text = data['bank']!; const int intervalSeconds = 2;
branchNameController.text = data['branch']!; int elapsed = 0;
String? foundName;
while (elapsed < timeoutSeconds) {
final String? name = await service.checkValidationStatus(refNo);
if (name != null && name.trim().isNotEmpty) {
foundName = name.trim();
break;
}
await Future.delayed(const Duration(seconds: intervalSeconds));
elapsed += intervalSeconds;
}
if (foundName != null) {
setState(() {
nameController.text = foundName!;
_isBeneficiaryValidated = true;
_validationError = null;
});
} else { } else {
bankNameController.clear(); setState(() {
branchNameController.clear(); _validationError = 'Beneficiary not found within timeout.';
ScaffoldMessenger.of(context).showSnackBar( _isBeneficiaryValidated = false;
SnackBar(content: Text(AppLocalizations.of(context).noIfscDetails)), });
}
} catch (e, st) {
// handle unexpected errors
// print or log if you want
debugPrint('Error validating beneficiary: $e\n$st');
setState(() {
_validationError = 'Something went wrong. Please try again.';
_isBeneficiaryValidated = false;
});
} finally {
if (mounted) {
setState(() {
_isValidating = false;
});
}
}
}
String _selectedAccountType = 'Savings'; // default value
void validateAndAddBeneficiary() async {
// Show spinner and disable UI
showDialog(
context: context,
barrierDismissible: false, // Prevent dismiss on tap outside
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async => false, // Disable back button
child: const Center(
child: CircularProgressIndicator(),
),
);
},
);
final beneficiary = Beneficiary(
accountNo: accountNumberController.text.trim(),
accountType: _selectedAccountType,
name: nameController.text.trim(),
ifscCode: ifscController.text.trim(),
);
var service = getIt<BeneficiaryService>();
try {
await service.sendForValidation(beneficiary);
bool isFound = await service.checkIfFound(beneficiary.accountNo);
if (context.mounted) {
Navigator.pop(context); // Close the spinner
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BeneficiaryResultPage(isSuccess: isFound),
),
); );
} }
} catch (e) {
Navigator.pop(context); // Close the spinner
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context).somethingWentWrong)),
);
}
}
/*
🔸 Optional: Use real IFSC API like:
final response = await http.get(Uri.parse('https://ifsc.razorpay.com/$ifsc'));
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
bankNameController.text = data['BANK'];
branchNameController.text = data['BRANCH'];
}
*/
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -170,19 +261,22 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
TextFormField( TextFormField(
controller: accountNumberController, controller: accountNumberController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: labelText: AppLocalizations.of(
AppLocalizations.of(context).accountNumber, context,
).accountNumber,
// prefixIcon: Icon(Icons.person), // prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
),
), ),
), ),
obscureText: true, obscureText: true,
@@ -190,8 +284,9 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
validator: (value) { validator: (value) {
if (value == null || value.length < 10) { if (value == null || value.length < 10) {
return AppLocalizations.of(context) return AppLocalizations.of(
.enterValidAccountNumber; context,
).enterValidAccountNumber;
} }
return null; return null;
}, },
@@ -201,178 +296,58 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
TextFormField( TextFormField(
controller: confirmAccountNumberController, controller: confirmAccountNumberController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context) labelText: AppLocalizations.of(
.confirmAccountNumber, context,
).confirmAccountNumber,
// prefixIcon: Icon(Icons.person), // prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
),
), ),
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return AppLocalizations.of(context) return AppLocalizations.of(
.reenterAccountNumber; context,
).reenterAccountNumber;
} }
if (value != accountNumberController.text) { if (value != accountNumberController.text) {
return AppLocalizations.of(context) return AppLocalizations.of(
.accountMismatch; context,
).accountMismatch;
} }
return null; return null;
}, },
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
TextFormField(
controller: nameController,
decoration: InputDecoration(
labelText: AppLocalizations.of(context).name,
// prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.next,
validator: (value) => value == null || value.isEmpty
? AppLocalizations.of(context).nameRequired
: null,
),
/*const SizedBox(height: 24),
TextFormField(
controller: bankNameController,
decoration: const InputDecoration(
labelText: 'Beneficiary Bank Name',
// prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.next,
validator: (value) =>
value == null || value.isEmpty ? "Bank name is required" : null,
),
const SizedBox(height: 24),
TextFormField(
controller: branchNameController,
decoration: const InputDecoration(
labelText: 'Branch Name',
// prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.next,
validator: (value) =>
value == null || value.isEmpty ? "Branch name is required" : null,
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
flex: 2,
child: TextFormField(
controller: ifscController,
decoration: const InputDecoration(
labelText: 'IFSC Code',
// prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.next,
validator: (value) => value == null || value.length < 5
? "Enter a valid IFSC"
: null,
),
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: DropdownButtonFormField<String>(
value: accountType,
decoration: const InputDecoration(
labelText: 'Account Type',
// prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
items: ['Savings', 'Current']
.map((type) => DropdownMenuItem(
value: type,
child: Text(type),
))
.toList(),
onChanged: (value) {
setState(() {
accountType = value!;
});
},
),
),
],
),*/
const SizedBox(height: 24),
// 🔹 IFSC Code Field // 🔹 IFSC Code Field
TextFormField( TextFormField(
controller: ifscController, controller: ifscController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).ifscCode, labelText: AppLocalizations.of(context).ifscCode,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
),
), ),
), ),
textCapitalization: TextCapitalization.characters, textCapitalization: TextCapitalization.characters,
@@ -392,10 +367,12 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
final pattern = RegExp(r'^[A-Z]{4}0[A-Z0-9]{6}$'); final pattern = RegExp(r'^[A-Z]{4}0[A-Z0-9]{6}$');
if (value == null || value.trim().isEmpty) { if (value == null || value.trim().isEmpty) {
return AppLocalizations.of(context).enterIfsc; return AppLocalizations.of(context).enterIfsc;
} else if (!pattern } else if (!pattern.hasMatch(
.hasMatch(value.trim().toUpperCase())) { value.trim().toUpperCase(),
return AppLocalizations.of(context) )) {
.invalidIfscFormat; return AppLocalizations.of(
context,
).invalidIfscFormat;
} }
return null; return null;
}, },
@@ -408,16 +385,18 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
enabled: false, // changed from readOnly to disabled enabled: false, // changed from readOnly to disabled
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).bankName, labelText: AppLocalizations.of(context).bankName,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, // disabled color fillColor: Theme.of(context).dialogBackgroundColor, // disabled color
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
),
), ),
), ),
), ),
@@ -429,46 +408,105 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
enabled: false, // changed from readOnly to disabled enabled: false, // changed from readOnly to disabled
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).branchName, labelText: AppLocalizations.of(context).branchName,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).dialogBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
), ),
), ),
), ),
),
//Validate Beneficiary Name
if (!_isBeneficiaryValidated)
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isValidating
? null
: () {
if (
confirmAccountNumberController.text ==
accountNumberController.text) {
_validateBeneficiary();
} else {
setState(() {
_validationError =
'Please enter a valid and matching account number.';
});
}
},
child: _isValidating
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Validate Beneficiary'),
),
),
),
//Beneficiary Name (Disabled)
TextFormField(
controller: nameController,
enabled: false,
decoration: InputDecoration(
labelText: AppLocalizations.of(context).beneficiaryName,
border: const OutlineInputBorder(),
isDense: true,
filled: true,
fillColor: Theme.of(context).dialogBackgroundColor,
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black),
),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2),
),
),
textInputAction: TextInputAction.next,
validator: (value) => value == null || value.isEmpty
? AppLocalizations.of(context).nameRequired
: null,
),
const SizedBox(height: 24), const SizedBox(height: 24),
// 🔹 Account Type Dropdown // 🔹 Account Type Dropdown
DropdownButtonFormField<String>( DropdownButtonFormField<String>(
value: accountType, value: accountType,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).accountType, labelText: AppLocalizations.of(context).accountType,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
), ),
), ),
items: [ ),
items:
[
AppLocalizations.of(context).savings, AppLocalizations.of(context).savings,
AppLocalizations.of(context).current AppLocalizations.of(context).current,
] ]
.map((type) => DropdownMenuItem( .map(
(type) => DropdownMenuItem(
value: type, value: type,
child: Text(type), child: Text(type),
)) ),
)
.toList(), .toList(),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@@ -483,17 +521,19 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).phone, labelText: AppLocalizations.of(context).phone,
prefixIcon: Icon(Icons.phone), prefixIcon: const Icon(Icons.phone),
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: borderSide: BorderSide(
BorderSide(color: Colors.black, width: 2), color: Colors.black,
width: 2,
),
), ),
), ),
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
@@ -513,12 +553,13 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
child: SizedBox( child: SizedBox(
width: 250, width: 250,
child: ElevatedButton( child: ElevatedButton(
onPressed: _submitForm, onPressed:
validateAndAddBeneficiary,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900], backgroundColor: Theme.of(context).primaryColorDark,
foregroundColor: Colors.white, foregroundColor: Theme.of(context).scaffoldBackgroundColor,
), ),
child: Text(AppLocalizations.of(context).validateAndAdd), child: Text(AppLocalizations.of(context).validateAndAdd),
), ),

View File

@@ -0,0 +1,115 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:confetti/confetti.dart';
import 'dart:math';
import '../../../app.dart';
import '../../../l10n/app_localizations.dart';
class BeneficiaryResultPage extends StatefulWidget {
final bool isSuccess;
const BeneficiaryResultPage({super.key, required this.isSuccess});
@override
State<BeneficiaryResultPage> createState() => _BeneficiaryResultPageState();
}
class _BeneficiaryResultPageState extends State<BeneficiaryResultPage> {
late ConfettiController _confettiController;
@override
void initState() {
super.initState();
_confettiController =
ConfettiController(duration: const Duration(seconds: 3));
if (widget.isSuccess) {
_confettiController.play();
}
}
@override
void dispose() {
_confettiController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final successAnimation = 'assets/animations/done.json';
final errorAnimation = 'assets/animations/error.json';
return Scaffold(
backgroundColor: widget.isSuccess ? Colors.green[50] : Colors.red[50],
body: Stack(
alignment: Alignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Lottie.asset(
widget.isSuccess ? successAnimation : errorAnimation,
width: 150,
height: 150,
repeat: false,
),
const SizedBox(height: 20),
Text(
widget.isSuccess
? AppLocalizations.of(context).beneficiaryAddedSuccess
: AppLocalizations.of(context).beneficiaryAdditionFailed,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: widget.isSuccess ? Colors.green : Colors.red,
),
textAlign: TextAlign.center,
),
],
),
Positioned(
bottom: 20, // keep it slightly above the very bottom
left: 16,
right: 16,
child: SizedBox(
height: 56, // larger button height
child: ElevatedButton(
onPressed: () {
Navigator.pushReplacement( // ensures back goes to ScaffoldScreen
context,
MaterialPageRoute(
builder: (context) => const NavigationScaffold(),
),
);
},
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 12),
backgroundColor: Theme.of(context).primaryColorDark,
foregroundColor: Theme.of(context).scaffoldBackgroundColor,
),
child: Text(
AppLocalizations.of(context).done,
style: const TextStyle(fontSize: 18), // slightly bigger text
),
),
),
),
if (widget.isSuccess)
Align(
alignment: Alignment.topCenter,
child: ConfettiWidget(
confettiController: _confettiController,
blastDirection: pi / 2,
maxBlastForce: 10,
minBlastForce: 5,
emissionFrequency: 0.05,
numberOfParticles: 20,
gravity: 0.2,
shouldLoop: false,
),
),
],
),
);
}
}

View File

@@ -1,10 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:kmobile/data/models/beneficiary.dart';
import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart'; import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../../../data/models/user.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import '../../../di/injection.dart';
import 'package:kmobile/api/services/beneficiary_service.dart';
import 'package:shimmer/shimmer.dart';
//import 'package:kmobile/data/models/user.dart';
class ManageBeneficiariesScreen extends StatefulWidget { class ManageBeneficiariesScreen extends StatefulWidget {
// final List<User> users;
// final int selectedIndex;
const ManageBeneficiariesScreen({super.key}); const ManageBeneficiariesScreen({super.key});
@override @override
@@ -13,77 +20,87 @@ class ManageBeneficiariesScreen extends StatefulWidget {
} }
class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> { class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> {
final List<Map<String, String>> beneficiaries = [ var service = getIt<BeneficiaryService>();
{ // late User selectedUser = widget.users[widget.selectedIndex];
'bank': 'State Bank Of India', //final BeneficiaryService _service = BeneficiaryService();
'name': 'Trina Bakshi', bool _isLoading = true;
int selectedAccountIndex = 0;
List<Beneficiary> _beneficiaries = [];
@override
void initState() {
super.initState();
_loadBeneficiaries();
}
Future<void> _loadBeneficiaries() async {
final data = await service.fetchBeneficiaryList();
setState(() {
_beneficiaries = data ;
_isLoading = false;
});
}
Widget _buildShimmerList() {
return ListView.builder(
itemCount: 6,
itemBuilder: (context, index) => Shimmer.fromColors(
baseColor: Colors.grey.shade300,
highlightColor: Colors.grey.shade100,
child: ListTile(
leading: const CircleAvatar(
radius: 24,
backgroundColor: Colors.white,
),
title: Container(
height: 16,
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 4),
),
subtitle: Container(
height: 14,
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 4),
),
),
),
);
}
Widget _buildBeneficiaryList() {
if (_beneficiaries.isEmpty) {
return Center(child: Text(AppLocalizations.of(context).noBeneficiaryFound));
}
return ListView.builder(
itemCount: _beneficiaries.length,
itemBuilder: (context, index) {
final item = _beneficiaries[index];
return ListTile(
leading: CircleAvatar(
radius: 24,
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.2),
child: Text(
item.name.isNotEmpty
? item.name[0].toUpperCase()
: '?',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
title: Text(item.name ?? 'Unknown'),
subtitle: Text(item.accountNo ?? 'No account number'),
);
}, },
{ );
'bank': 'State Bank Of India', }
'name': 'Sheetal Rao',
},
{
'bank': 'Punjab National Bank',
'name': 'Manoj Kumar',
},
{
'bank': 'State Bank Of India',
'name': 'Rohit Mehra',
},
];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
leading: IconButton( title: Text(AppLocalizations.of(context).beneficiaries),
icon: const Icon(Symbols.arrow_back_ios_new),
onPressed: () {
Navigator.pop(context);
},
),
title: Text(
AppLocalizations.of(context).beneficiaries,
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
),
centerTitle: false,
actions: [
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: CircleAvatar(
backgroundColor: Colors.grey[200],
radius: 20,
child: SvgPicture.asset(
'assets/images/avatar_male.svg',
width: 40,
height: 40,
fit: BoxFit.cover,
),
),
),
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: beneficiaries.length,
itemBuilder: (context, index) {
final beneficiary = beneficiaries[index];
return ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.blue, child: Text('A')),
title: Text(beneficiary['name']!),
subtitle: Text(beneficiary['bank']!),
trailing: IconButton(
icon: const Icon(Symbols.delete_forever, color: Colors.red),
onPressed: () {
// Delete action
},
),
);
},
),
), ),
body: _isLoading ? _buildShimmerList() : _buildBeneficiaryList(),
floatingActionButton: Padding( floatingActionButton: Padding(
padding: const EdgeInsets.only(bottom: 8.0), padding: const EdgeInsets.only(bottom: 8.0),
child: FloatingActionButton( child: FloatingActionButton(
@@ -91,10 +108,12 @@ class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const AddBeneficiaryScreen())); builder: (context) => AddBeneficiaryScreen(),
),
);
}, },
backgroundColor: Colors.grey[300], backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.blue[900], foregroundColor: Theme.of(context).primaryColor,
elevation: 5, elevation: 5,
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
class BlockCardScreen extends StatefulWidget { class BlockCardScreen extends StatefulWidget {
@@ -42,7 +42,7 @@ class _BlockCardScreen extends State<BlockCardScreen> {
onPressed: () { onPressed: () {
// Just close the SnackBar // Just close the SnackBar
}, },
textColor: Colors.white, textColor: Theme.of(context).dialogBackgroundColor,
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
@@ -97,7 +97,7 @@ class _BlockCardScreen extends State<BlockCardScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -122,7 +122,7 @@ class _BlockCardScreen extends State<BlockCardScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -150,7 +150,7 @@ class _BlockCardScreen extends State<BlockCardScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -174,7 +174,7 @@ class _BlockCardScreen extends State<BlockCardScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -198,8 +198,8 @@ class _BlockCardScreen extends State<BlockCardScreen> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900], backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white, foregroundColor: Theme.of(context).scaffoldBackgroundColor,
), ),
child: Text(AppLocalizations.of(context).block), child: Text(AppLocalizations.of(context).block),
), ),

View File

@@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:kmobile/features/card/screens/block_card_screen.dart'; import 'package:kmobile/features/card/screens/block_card_screen.dart';
import 'package:kmobile/features/card/screens/card_pin_change_details_screen.dart'; import 'package:kmobile/features/card/screens/card_pin_change_details_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 CardManagementScreen extends StatefulWidget { class CardManagementScreen extends StatefulWidget {
const CardManagementScreen({super.key}); const CardManagementScreen({super.key});
@@ -46,9 +46,7 @@ class _CardManagementScreen extends State<CardManagementScreen> {
label: AppLocalizations.of(context).applyDebitCard, label: AppLocalizations.of(context).applyDebitCard,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
CardManagementTile( CardManagementTile(
icon: Symbols.remove_moderator, icon: Symbols.remove_moderator,
label: AppLocalizations.of(context).blockUnblockCard, label: AppLocalizations.of(context).blockUnblockCard,
@@ -56,12 +54,12 @@ class _CardManagementScreen extends State<CardManagementScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const BlockCardScreen())); builder: (context) => const BlockCardScreen(),
),
);
}, },
), ),
const Divider( const Divider(height: 1),
height: 1,
),
CardManagementTile( CardManagementTile(
icon: Symbols.password_2, icon: Symbols.password_2,
label: AppLocalizations.of(context).changeCardPin, label: AppLocalizations.of(context).changeCardPin,
@@ -69,13 +67,12 @@ class _CardManagementScreen extends State<CardManagementScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const CardPinChangeDetailsScreen(),
const CardPinChangeDetailsScreen())); ),
);
}, },
), ),
const Divider( const Divider(height: 1),
height: 1,
),
], ],
), ),
); );

View File

@@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:kmobile/features/card/screens/card_pin_set_screen.dart'; import 'package:kmobile/features/card/screens/card_pin_set_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 CardPinChangeDetailsScreen extends StatefulWidget { class CardPinChangeDetailsScreen extends StatefulWidget {
const CardPinChangeDetailsScreen({super.key}); const CardPinChangeDetailsScreen({super.key});
@@ -87,7 +87,7 @@ class _CardPinChangeDetailsScreen extends State<CardPinChangeDetailsScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -112,7 +112,7 @@ class _CardPinChangeDetailsScreen extends State<CardPinChangeDetailsScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -140,7 +140,7 @@ class _CardPinChangeDetailsScreen extends State<CardPinChangeDetailsScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -164,7 +164,7 @@ class _CardPinChangeDetailsScreen extends State<CardPinChangeDetailsScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -188,8 +188,8 @@ class _CardPinChangeDetailsScreen extends State<CardPinChangeDetailsScreen> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900], backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white, foregroundColor: Theme.of(context).scaffoldBackgroundColor,
), ),
child: Text(AppLocalizations.of(context).next), child: Text(AppLocalizations.of(context).next),
), ),

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 CardPinSetScreen extends StatefulWidget { class CardPinSetScreen extends StatefulWidget {
const CardPinSetScreen({super.key}); const CardPinSetScreen({super.key});
@@ -25,7 +25,7 @@ class _CardPinSetScreen extends State<CardPinSetScreen> {
onPressed: () { onPressed: () {
// Just close the SnackBar // Just close the SnackBar
}, },
textColor: Colors.white, textColor: Theme.of(context).dialogBackgroundColor,
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
@@ -87,7 +87,7 @@ class _CardPinSetScreen extends State<CardPinSetScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -116,7 +116,7 @@ class _CardPinSetScreen extends State<CardPinSetScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -143,8 +143,8 @@ class _CardPinSetScreen extends State<CardPinSetScreen> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900], backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white, foregroundColor: Theme.of(context).scaffoldBackgroundColor,
), ),
child: Text(AppLocalizations.of(context).submit), child: Text(AppLocalizations.of(context).submit),
), ),

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:kmobile/features/enquiry/screens/enquiry_screen.dart'; import 'package:kmobile/features/enquiry/screens/enquiry_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 ChequeManagementScreen extends StatefulWidget { class ChequeManagementScreen extends StatefulWidget {
const ChequeManagementScreen({super.key}); const ChequeManagementScreen({super.key});
@@ -51,54 +51,42 @@ class _ChequeManagementScreen extends State<ChequeManagementScreen> {
label: AppLocalizations.of(context).requestChequeBook, label: AppLocalizations.of(context).requestChequeBook,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ChequeManagementTile( ChequeManagementTile(
icon: Symbols.data_alert, icon: Symbols.data_alert,
label: AppLocalizations.of(context).enquiry, label: AppLocalizations.of(context).enquiry,
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (context) => const EnquiryScreen()),
builder: (context) => const EnquiryScreen())); );
}, },
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ChequeManagementTile( ChequeManagementTile(
icon: Symbols.approval_delegation, icon: Symbols.approval_delegation,
label: AppLocalizations.of(context).chequeDeposit, label: AppLocalizations.of(context).chequeDeposit,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ChequeManagementTile( ChequeManagementTile(
icon: Symbols.front_hand, icon: Symbols.front_hand,
label: AppLocalizations.of(context).stopCheque, label: AppLocalizations.of(context).stopCheque,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ChequeManagementTile( ChequeManagementTile(
icon: Symbols.cancel_presentation, icon: Symbols.cancel_presentation,
label: AppLocalizations.of(context).revokeStop, label: AppLocalizations.of(context).revokeStop,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ChequeManagementTile( ChequeManagementTile(
icon: Symbols.payments, icon: Symbols.payments,
label: AppLocalizations.of(context).positivePay, label: AppLocalizations.of(context).positivePay,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
], ],
), ),
); );

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/data/models/user.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 CustomerInfoScreen extends StatefulWidget { class CustomerInfoScreen extends StatefulWidget {
final User user; final User user;
@@ -70,43 +70,50 @@ class _CustomerInfoScreenState extends State<CustomerInfoScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w500), fontWeight: FontWeight.w500,
),
), ),
), ),
Text( Text(
'${AppLocalizations.of(context).cif}: ${user.cifNumber ?? 'N/A'}', '${AppLocalizations.of(context).cif}: ${user.cifNumber ?? 'N/A'}',
style: style: const TextStyle(fontSize: 16, color: Colors.grey),
const TextStyle(fontSize: 16, color: Colors.grey),
), ),
SizedBox(height: 30), SizedBox(height: 30),
InfoField( InfoField(
label: AppLocalizations.of(context).activeAccounts, label: AppLocalizations.of(context).activeAccounts,
value: user.activeAccounts?.toString() ?? '6'), value: user.activeAccounts?.toString() ?? '6',
),
InfoField( InfoField(
label: AppLocalizations.of(context).mobileNumber, label: AppLocalizations.of(context).mobileNumber,
value: user.mobileNo ?? 'N/A'), value: user.mobileNo ?? 'N/A',
),
InfoField( InfoField(
label: AppLocalizations.of(context).dateOfBirth, label: AppLocalizations.of(context).dateOfBirth,
value: (user.dateOfBirth != null && value:
(user.dateOfBirth != null &&
user.dateOfBirth!.length == 8) user.dateOfBirth!.length == 8)
? '${user.dateOfBirth!.substring(0, 2)}-${user.dateOfBirth!.substring(2, 4)}-${user.dateOfBirth!.substring(4, 8)}' ? '${user.dateOfBirth!.substring(0, 2)}-${user.dateOfBirth!.substring(2, 4)}-${user.dateOfBirth!.substring(4, 8)}'
: 'N/A'), // Replace with DOB if available : 'N/A',
), // Replace with DOB if available
InfoField( InfoField(
label: AppLocalizations.of(context).branchCode, label: AppLocalizations.of(context).branchCode,
value: user.branchId ?? 'N/A'), value: user.branchId ?? 'N/A',
),
InfoField( InfoField(
label: AppLocalizations.of(context).branchAddress, label: AppLocalizations.of(context).branchAddress,
value: user.address ?? value: user.address ?? 'N/A',
'N/A'), // Replace with Aadhar if available ), // Replace with Aadhar if available
InfoField( InfoField(
label: AppLocalizations.of(context).primaryId, label: AppLocalizations.of(context).primaryId,
value: user.primaryId ?? value: user.primaryId ?? 'N/A',
'N/A'), // Replace with PAN if available ), // Replace with PAN if available
], ],
), ),
), ),
), ),
))); ),
),
);
} }
} }
@@ -135,10 +142,7 @@ class InfoField extends StatelessWidget {
const SizedBox(height: 3), const SizedBox(height: 3),
Text( Text(
value, value,
style: const TextStyle( style: const TextStyle(fontSize: 16, color: Colors.black),
fontSize: 16,
color: Colors.black,
),
), ),
], ],
), ),

View File

@@ -20,7 +20,7 @@ import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:kmobile/data/models/transaction.dart'; import 'package:kmobile/data/models/transaction.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
class DashboardScreen extends StatefulWidget { class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key}); const DashboardScreen({super.key});
@@ -57,13 +57,20 @@ class _DashboardScreenState extends State<DashboardScreen> {
setState(() => _transactions = fiveTxns); setState(() => _transactions = fiveTxns);
} catch (e) { } catch (e) {
log(accountNo, error: e); log(accountNo, error: e);
ScaffoldMessenger.of(context).showSnackBar(SnackBar( if (!mounted) return;
content: ScaffoldMessenger.of(context).showSnackBar(
Text(AppLocalizations.of(context).failedToLoad(e.toString())))); SnackBar(
content: Text(
AppLocalizations.of(context).failedToLoad(e.toString()),
),
),
);
} finally { } finally {
if (mounted) {
setState(() => _txLoading = false); setState(() => _txLoading = false);
} }
} }
}
Future<void> _refreshAccountData(BuildContext context) async { Future<void> _refreshAccountData(BuildContext context) async {
setState(() { setState(() {
@@ -75,7 +82,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
/*const*/ SnackBar( /*const*/ SnackBar(
content: Text(AppLocalizations.of(context).failedToRefresh)), content: Text(AppLocalizations.of(context).failedToRefresh),
),
); );
} }
setState(() { setState(() {
@@ -85,13 +93,9 @@ class _DashboardScreenState extends State<DashboardScreen> {
Widget _buildBalanceShimmer() { Widget _buildBalanceShimmer() {
return Shimmer.fromColors( return Shimmer.fromColors(
baseColor: Colors.white.withOpacity(0.7), baseColor: Theme.of(context).dialogBackgroundColor,
highlightColor: Colors.white.withOpacity(0.3), highlightColor: Theme.of(context).dialogBackgroundColor,
child: Container( child: Container(width: 100, height: 32, color: Theme.of(context).scaffoldBackgroundColor),
width: 100,
height: 32,
color: Colors.white,
),
); );
} }
@@ -106,7 +110,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
'dr.', 'dr.',
'shri', 'shri',
'smt.', 'smt.',
'kumari' 'kumari',
]; ];
String processed = name.trim().toLowerCase(); String processed = name.trim().toLowerCase();
for (final title in titles) { for (final title in titles) {
@@ -151,6 +155,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
await storage.write('biometric_prompt_shown', 'true'); await storage.write('biometric_prompt_shown', 'true');
if (!mounted) return;
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text(AppLocalizations.of(context).later), child: Text(AppLocalizations.of(context).later),
@@ -192,16 +197,18 @@ class _DashboardScreenState extends State<DashboardScreen> {
} }
}, },
child: Scaffold( child: Scaffold(
backgroundColor: const Color(0xfff5f9fc), backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: AppBar( appBar: AppBar(
backgroundColor: const Color(0xfff5f9fc), backgroundColor:Theme.of(context).scaffoldBackgroundColor,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
title: Text( title: Text(
AppLocalizations.of(context).kMobile, AppLocalizations.of(context).kconnect,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w500), fontWeight: FontWeight.w500,
), ),
),
centerTitle: true,
actions: [ actions: [
Padding( Padding(
padding: const EdgeInsets.only(right: 10.0), padding: const EdgeInsets.only(right: 10.0),
@@ -211,7 +218,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const ProfileScreen()), builder: (context) => const ProfileScreen(),
),
); );
}, },
child: CircleAvatar( child: CircleAvatar(
@@ -228,7 +236,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
], ],
), ),
body: BlocBuilder<AuthCubit, AuthState>(builder: (context, state) { body: BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
if (state is AuthLoading || state is AuthInitial) { if (state is AuthLoading || state is AuthInitial) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
@@ -257,8 +266,9 @@ class _DashboardScreenState extends State<DashboardScreen> {
"${AppLocalizations.of(context).hi} $firstName", "${AppLocalizations.of(context).hi} $firstName",
style: GoogleFonts.montserrat().copyWith( style: GoogleFonts.montserrat().copyWith(
fontSize: 25, fontSize: 25,
color: Theme.of(context).primaryColorDark, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w700), fontWeight: FontWeight.w700,
),
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -266,7 +276,9 @@ class _DashboardScreenState extends State<DashboardScreen> {
// Account Info Card // Account Info Card
Container( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 18, vertical: 10), horizontal: 18,
vertical: 10,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
@@ -276,24 +288,32 @@ class _DashboardScreenState extends State<DashboardScreen> {
children: [ children: [
Row( Row(
children: [ children: [
Text(AppLocalizations.of(context).accountNumber, Text(
"${AppLocalizations.of(context).accountNumber}: ",
style: TextStyle( style: TextStyle(
color: Colors.white, fontSize: 12)), color: Theme.of(context).dialogBackgroundColor,
fontSize: 12,
),
),
DropdownButton<int>( DropdownButton<int>(
value: selectedAccountIndex, value: selectedAccountIndex,
dropdownColor: Theme.of(context).primaryColor, dropdownColor: Theme.of(context).primaryColor,
underline: const SizedBox(), underline: const SizedBox(),
icon: const Icon(Icons.keyboard_arrow_down), icon: const Icon(Icons.keyboard_arrow_down),
iconEnabledColor: Colors.white, iconEnabledColor:Theme.of(context).dialogBackgroundColor,
style: const TextStyle( style: TextStyle(
color: Colors.white, fontSize: 14), color: Theme.of(context).dialogBackgroundColor,
fontSize: 14,
),
items: List.generate(users.length, (index) { items: List.generate(users.length, (index) {
return DropdownMenuItem<int>( return DropdownMenuItem<int>(
value: index, value: index,
child: Text( child: Text(
users[index].accountNo ?? 'N/A', users[index].accountNo ?? 'N/A',
style: const TextStyle( style: TextStyle(
color: Colors.white, fontSize: 14), color: Theme.of(context).dialogBackgroundColor,
fontSize: 14,
),
), ),
); );
}), }),
@@ -309,7 +329,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
selectedAccountIndex = newIndex; selectedAccountIndex = newIndex;
}); });
await Future.delayed( await Future.delayed(
const Duration(milliseconds: 200)); const Duration(milliseconds: 200),
);
setState(() { setState(() {
isBalanceLoading = false; isBalanceLoading = false;
}); });
@@ -319,22 +340,25 @@ class _DashboardScreenState extends State<DashboardScreen> {
}); });
} }
await _loadTransactions( await _loadTransactions(
users[newIndex].accountNo!); users[newIndex].accountNo!,
);
}, },
), ),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: isRefreshing icon: isRefreshing
? const SizedBox( ? SizedBox(
width: 20, width: 20,
height: 20, height: 20,
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Colors.white, color: Theme.of(context).dialogBackgroundColor,
strokeWidth: 2, strokeWidth: 2,
), ),
) )
: const Icon(Icons.refresh, : Icon(
color: Colors.white), Icons.refresh,
color: Theme.of(context).dialogBackgroundColor,
),
onPressed: isRefreshing onPressed: isRefreshing
? null ? null
: () => _refreshAccountData(context), : () => _refreshAccountData(context),
@@ -344,28 +368,36 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
Text( Text(
getFullAccountType(currAccount.accountType), getFullAccountType(currAccount.accountType),
style: const TextStyle( style: TextStyle(
color: Colors.white, fontSize: 16), color: Theme.of(context).dialogBackgroundColor,
fontSize: 16,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
const Text("", Text(
"",
style: TextStyle( style: TextStyle(
color: Colors.white, color: Theme.of(context).dialogBackgroundColor,
fontSize: 40, fontSize: 40,
fontWeight: FontWeight.w700)), fontWeight: FontWeight.w700,
),
),
isRefreshing || isBalanceLoading isRefreshing || isBalanceLoading
? _buildBalanceShimmer() ? _buildBalanceShimmer()
: Text( : Text(
isVisible isVisible
? currAccount.currentBalance ?? '0.00' ? currAccount.currentBalance ??
'0.00'
: '********', : '********',
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Theme.of(context).dialogBackgroundColor,
fontSize: 40, fontSize: 40,
fontWeight: FontWeight.w700)), fontWeight: FontWeight.w700,
),
),
const Spacer(), const Spacer(),
InkWell( InkWell(
onTap: () async { onTap: () async {
@@ -375,7 +407,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
isBalanceLoading = true; isBalanceLoading = true;
}); });
await Future.delayed( await Future.delayed(
const Duration(seconds: 1)); const Duration(seconds: 1),
);
setState(() { setState(() {
isVisible = true; isVisible = true;
isBalanceLoading = false; isBalanceLoading = false;
@@ -390,7 +423,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
isVisible isVisible
? Symbols.visibility_lock ? Symbols.visibility_lock
: Symbols.visibility, : Symbols.visibility,
color: Colors.white), color: Theme.of(context).scaffoldBackgroundColor,
),
), ),
], ],
), ),
@@ -401,7 +435,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
const SizedBox(height: 18), const SizedBox(height: 18),
Text( Text(
AppLocalizations.of(context).quickLinks, AppLocalizations.of(context).quickLinks,
style: TextStyle(fontSize: 17), style: const TextStyle(fontSize: 17),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -411,41 +445,61 @@ class _DashboardScreenState extends State<DashboardScreen> {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: [ children: [
_buildQuickLink(Symbols.id_card, _buildQuickLink(
AppLocalizations.of(context).customerInfo, () { Symbols.id_card,
AppLocalizations.of(context).customerInfo,
() {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => CustomerInfoScreen( builder: (context) => CustomerInfoScreen(
user: users[selectedAccountIndex], user: users[selectedAccountIndex],
))); ),
}), ),
_buildQuickLink(Symbols.currency_rupee, );
AppLocalizations.of(context).quickPay, () { },
),
_buildQuickLink(
Symbols.currency_rupee,
AppLocalizations.of(context).quickPay,
() {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => QuickPayScreen( builder: (context) => QuickPayScreen(
debitAccount: currAccount.accountNo!))); debitAccount: currAccount.accountNo!,
}), ),
_buildQuickLink(Symbols.send_money, ),
AppLocalizations.of(context).fundTransfer, () { );
},
),
_buildQuickLink(
Symbols.send_money,
AppLocalizations.of(context).fundTransfer,
() {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
const FundTransferBeneficiaryScreen())); const FundTransferBeneficiaryScreen()));
}, disable: false), }, disable: true),
_buildQuickLink(Symbols.server_person, _buildQuickLink(Symbols.server_person,
AppLocalizations.of(context).accountInfo, () { AppLocalizations.of(context).accountInfo, () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AccountInfoScreen( builder: (context) => AccountInfoScreen(
user: users[selectedAccountIndex]))); users: users,
}), selectedIndex: selectedAccountIndex,
_buildQuickLink(Symbols.receipt_long, ),
AppLocalizations.of(context).accountStatement, () { ),
);
},
),
_buildQuickLink(
Symbols.receipt_long,
AppLocalizations.of(context).accountStatement,
() {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
@@ -456,7 +510,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
}), }),
_buildQuickLink(Symbols.checkbook, _buildQuickLink(Symbols.checkbook,
AppLocalizations.of(context).handleCheque, () {}, AppLocalizations.of(context).handleCheque, () {},
disable: false), disable: true),
_buildQuickLink(Icons.group, _buildQuickLink(Icons.group,
AppLocalizations.of(context).manageBeneficiary, () { AppLocalizations.of(context).manageBeneficiary, () {
Navigator.push( Navigator.push(
@@ -479,13 +533,14 @@ class _DashboardScreenState extends State<DashboardScreen> {
// Recent Transactions // Recent Transactions
Text( Text(
AppLocalizations.of(context).recentTransactions, AppLocalizations.of(context).recentTransactions,
style: TextStyle(fontSize: 17), style: const TextStyle(fontSize: 17),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
if (_txLoading) if (_txLoading)
..._buildTransactionShimmer() ..._buildTransactionShimmer()
else if (_transactions.isNotEmpty) else if (_transactions.isNotEmpty)
..._transactions.map((tx) => ListTile( ..._transactions.map(
(tx) => ListTile(
leading: Icon( leading: Icon(
tx.type == 'CR' tx.type == 'CR'
? Symbols.call_received ? Symbols.call_received
@@ -501,11 +556,16 @@ class _DashboardScreenState extends State<DashboardScreen> {
: '', : '',
style: const TextStyle(fontSize: 14), style: const TextStyle(fontSize: 14),
), ),
subtitle: Text(tx.date ?? '', subtitle: Text(
style: const TextStyle(fontSize: 12)), tx.date ?? '',
trailing: Text("${tx.amount}", style: const TextStyle(fontSize: 12),
style: const TextStyle(fontSize: 16)), ),
)) trailing: Text(
"${tx.amount}",
style: const TextStyle(fontSize: 16),
),
),
)
else else
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0), padding: const EdgeInsets.symmetric(vertical: 24.0),
@@ -525,8 +585,10 @@ class _DashboardScreenState extends State<DashboardScreen> {
); );
} }
return Center( return Center(
child: Text(AppLocalizations.of(context).somethingWentWrong)); child: Text(AppLocalizations.of(context).somethingWentWrong),
}), );
},
),
), ),
); );
} }
@@ -537,36 +599,44 @@ class _DashboardScreenState extends State<DashboardScreen> {
leading: Shimmer.fromColors( leading: Shimmer.fromColors(
baseColor: Colors.grey[300]!, baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!, highlightColor: Colors.grey[100]!,
child: const CircleAvatar(radius: 12, backgroundColor: Colors.white), child: CircleAvatar(radius: 12, backgroundColor: Theme.of(context).scaffoldBackgroundColor),
), ),
title: Shimmer.fromColors( title: Shimmer.fromColors(
baseColor: Colors.grey[300]!, baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!, highlightColor: Colors.grey[100]!,
child: Container(height: 10, width: 100, color: Colors.white), child: Container(height: 10, width: 100, color: Theme.of(context).scaffoldBackgroundColor),
), ),
subtitle: Shimmer.fromColors( subtitle: Shimmer.fromColors(
baseColor: Colors.grey[300]!, baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!, highlightColor: Colors.grey[100]!,
child: Container(height: 8, width: 60, color: Colors.white), child: Container(height: 8, width: 60, color: Theme.of(context).scaffoldBackgroundColor),
), ),
); );
}); });
} }
Widget _buildQuickLink(IconData icon, String label, VoidCallback onTap, Widget _buildQuickLink(
{bool disable = false}) { IconData icon,
String label,
VoidCallback onTap, {
bool disable = false,
}) {
return InkWell( return InkWell(
onTap: disable ? null : onTap, onTap: disable ? null : onTap,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(icon, Icon(
icon,
size: 30, size: 30,
color: disable ? Colors.grey : Theme.of(context).primaryColor), color: disable ? Colors.grey : Theme.of(context).primaryColor,
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text(label, Text(
label,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle(fontSize: 13)), style: const TextStyle(fontSize: 13),
),
], ],
), ),
); );

View File

@@ -1,14 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../accounts/models/account.dart'; import '../../accounts/models/account.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../../../l10n/app_localizations.dart';
class AccountCard extends StatelessWidget { class AccountCard extends StatelessWidget {
final Account account; final Account account;
const AccountCard({ const AccountCard({super.key, required this.account});
super.key,
required this.account,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -42,8 +39,8 @@ class AccountCard extends StatelessWidget {
children: [ children: [
Text( Text(
account.accountType, account.accountType,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Theme.of(context).scaffoldBackgroundColor,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -52,23 +49,20 @@ class AccountCard extends StatelessWidget {
account.accountType == 'Savings' account.accountType == 'Savings'
? Icons.savings ? Icons.savings
: Icons.account_balance, : Icons.account_balance,
color: Colors.white, color: Theme.of(context).scaffoldBackgroundColor,
), ),
], ],
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
account.accountNumber, account.accountNumber,
style: const TextStyle( style: TextStyle(color: Theme.of(context).dialogBackgroundColor, fontSize: 16),
color: Colors.white70,
fontSize: 16,
),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
Text( Text(
'${account.currency} ${account.balance.toStringAsFixed(2)}', '${account.currency} ${account.balance.toStringAsFixed(2)}',
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Theme.of(context).scaffoldBackgroundColor,
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -76,10 +70,7 @@ class AccountCard extends StatelessWidget {
const SizedBox(height: 5), const SizedBox(height: 5),
Text( Text(
AppLocalizations.of(context).availableBalance, AppLocalizations.of(context).availableBalance,
style: TextStyle( style: TextStyle(color: Theme.of(context).dialogBackgroundColor, fontSize: 12),
color: Colors.white70,
fontSize: 12,
),
), ),
], ],
), ),

View File

@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
class EnquiryScreen extends StatefulWidget { class EnquiryScreen extends StatefulWidget {
const EnquiryScreen({super.key}); const EnquiryScreen({super.key});
@@ -38,12 +38,12 @@ class _EnquiryScreen extends State<EnquiryScreen> {
const SizedBox(height: 4), const SizedBox(height: 4),
GestureDetector( GestureDetector(
onTap: () => _launchEmailAddress(email), onTap: () => _launchEmailAddress(email),
child: Text(email, style: const TextStyle(color: Colors.blue)), child: Text(email, style: TextStyle(color: Theme.of(context).primaryColor)),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
GestureDetector( GestureDetector(
onTap: () => _launchPhoneNumber(phone), onTap: () => _launchPhoneNumber(phone),
child: Text(phone, style: const TextStyle(color: Colors.blue)), child: Text(phone, style: TextStyle(color: Theme.of(context).scaffoldBackgroundColor)),
), ),
], ],
); );
@@ -86,14 +86,15 @@ class _EnquiryScreen extends State<EnquiryScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// … existing Mail us / Call us / Write to us … // … existing Mail us / Call us / Write to us …
const SizedBox(height: 20), const SizedBox(height: 20),
Text(AppLocalizations.of(context).writeToUs, Text(
style: TextStyle(color: Colors.grey)), AppLocalizations.of(context).writeToUs,
style: TextStyle(color: Colors.grey),
),
const SizedBox(height: 4), const SizedBox(height: 4),
const Text( Text(
"complaint@kccb.in", "complaint@kccb.in",
style: TextStyle(color: Colors.blue), style: TextStyle(color: Theme.of(context).primaryColor),
), ),
SizedBox(height: 20), SizedBox(height: 20),

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; /*import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart'; // import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
import 'package:kmobile/features/fund_transfer/screens/fund_transfer_screen.dart'; import 'package:kmobile/features/fund_transfer/screens/fund_transfer_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 { class FundTransferBeneficiaryScreen extends StatefulWidget {
const FundTransferBeneficiaryScreen({super.key}); const FundTransferBeneficiaryScreen({super.key});
@@ -16,22 +16,10 @@ class FundTransferBeneficiaryScreen extends StatefulWidget {
class _FundTransferBeneficiaryScreen class _FundTransferBeneficiaryScreen
extends State<FundTransferBeneficiaryScreen> { extends State<FundTransferBeneficiaryScreen> {
final List<Map<String, String>> beneficiaries = [ final List<Map<String, String>> beneficiaries = [
{ {'bank': 'State Bank Of India', 'name': 'Trina Bakshi'},
'bank': 'State Bank Of India', {'bank': 'State Bank Of India', 'name': 'Sheetal Rao'},
'name': 'Trina Bakshi', {'bank': 'Punjab National Bank', 'name': 'Manoj Kumar'},
}, {'bank': 'State Bank Of India', 'name': 'Rohit Mehra'},
{
'bank': 'State Bank Of India',
'name': 'Sheetal Rao',
},
{
'bank': 'Punjab National Bank',
'name': 'Manoj Kumar',
},
{
'bank': 'State Bank Of India',
'name': 'Rohit Mehra',
},
]; ];
@override @override
@@ -72,15 +60,14 @@ class _FundTransferBeneficiaryScreen
itemBuilder: (context, index) { itemBuilder: (context, index) {
final beneficiary = beneficiaries[index]; final beneficiary = beneficiaries[index];
return ListTile( return ListTile(
leading: const CircleAvatar( leading: CircleAvatar(
backgroundColor: Colors.blue, child: Text('A')), backgroundColor: Theme.of(context).primaryColor,
child: Text('A'),
),
title: Text(beneficiary['name']!), title: Text(beneficiary['name']!),
subtitle: Text(beneficiary['bank']!), subtitle: Text(beneficiary['bank']!),
trailing: IconButton( trailing: IconButton(
icon: const Icon( icon: const Icon(Symbols.arrow_right, size: 20),
Symbols.arrow_right,
size: 20,
),
onPressed: () { onPressed: () {
// Delete action // Delete action
}, },
@@ -89,27 +76,114 @@ class _FundTransferBeneficiaryScreen
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const FundTransferScreen())); builder: (context) => const FundTransferScreen(),
),
);
}, },
); );
}, },
), ),
), ),
floatingActionButton: Padding( );
padding: const EdgeInsets.only(bottom: 8.0), }
child: FloatingActionButton( }*/
onPressed: () {
Navigator.push( import 'package:flutter/material.dart';
context, import 'package:kmobile/data/models/beneficiary.dart';
MaterialPageRoute( //import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
builder: (context) => const AddBeneficiaryScreen())); import '../../../l10n/app_localizations.dart';
}, import '../../../di/injection.dart';
backgroundColor: Colors.grey[300], import 'package:kmobile/api/services/beneficiary_service.dart';
foregroundColor: Colors.blue[900], import 'package:shimmer/shimmer.dart';
elevation: 5,
child: const Icon(Icons.add),
), class FundTransferBeneficiaryScreen extends StatefulWidget {
), const FundTransferBeneficiaryScreen({super.key});
@override
State<FundTransferBeneficiaryScreen> createState() =>
_ManageBeneficiariesScreen();
}
class _ManageBeneficiariesScreen extends State<FundTransferBeneficiaryScreen> {
var service = getIt<BeneficiaryService>();
//final BeneficiaryService _service = BeneficiaryService();
bool _isLoading = true;
List<Beneficiary> _beneficiaries = [];
@override
void initState() {
super.initState();
_loadBeneficiaries();
}
Future<void> _loadBeneficiaries() async {
final data = await service.fetchBeneficiaryList();
setState(() {
_beneficiaries = data ;
_isLoading = false;
});
}
Widget _buildShimmerList() {
return ListView.builder(
itemCount: 6,
itemBuilder: (context, index) => Shimmer.fromColors(
baseColor: Colors.grey.shade300,
highlightColor: Colors.grey.shade100,
child: ListTile(
leading: CircleAvatar(
radius: 24,
backgroundColor: Colors.white,
),
title: Container(
height: 16,
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 4),
),
subtitle: Container(
height: 14,
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 4),
),
),
),
);
}
Widget _buildBeneficiaryList() {
if (_beneficiaries.isEmpty) {
return Center(child: Text(AppLocalizations.of(context).noBeneficiaryFound));
}
return ListView.builder(
itemCount: _beneficiaries.length,
itemBuilder: (context, index) {
final item = _beneficiaries[index];
return ListTile(
leading: CircleAvatar(
radius: 24,
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.2),
child: Text(
item.name.isNotEmpty
? item.name[0].toUpperCase()
: '?',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
title: Text(item.name ?? 'Unknown'),
subtitle: Text(item.accountNo ?? 'No account number'),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).beneficiaries),
),
body: _isLoading ? _buildShimmerList() : _buildBeneficiaryList(),
); );
} }
} }

View File

@@ -1,8 +1,7 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.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: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 { class FundTransferScreen extends StatefulWidget {
const FundTransferScreen({super.key}); const FundTransferScreen({super.key});
@@ -46,13 +45,16 @@ class _FundTransferScreen extends State<FundTransferScreen> {
} else if (value == 'back') { } else if (value == 'back') {
return GestureDetector( return GestureDetector(
onTap: () => onKeyTap(value), onTap: () => onKeyTap(value),
child: const Icon(Symbols.backspace, size: 30)); child: const Icon(Symbols.backspace, size: 30),
);
} else { } else {
return GestureDetector( return GestureDetector(
onTap: () => onKeyTap(value), onTap: () => onKeyTap(value),
child: Center( child: Center(
child: Text(value, child: Text(
style: const TextStyle(fontSize: 24, color: Colors.black)), value,
style: const TextStyle(fontSize: 24, color: Colors.black),
),
), ),
); );
} }
@@ -109,12 +111,14 @@ class _FundTransferScreen extends State<FundTransferScreen> {
Text( Text(
'0300015678903456', '0300015678903456',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500), style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
) ),
], ],
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Text(AppLocalizations.of(context).enterAmount, Text(
style: TextStyle(fontSize: 20)), AppLocalizations.of(context).enterAmount,
style: TextStyle(fontSize: 20),
),
const SizedBox(height: 20), const SizedBox(height: 20),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
@@ -130,7 +134,7 @@ class _FundTransferScreen extends State<FundTransferScreen> {
const Spacer(), const Spacer(),
Container( Container(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
color: Colors.white, color: Theme.of(context).scaffoldBackgroundColor,
child: GridView.count( child: GridView.count(
crossAxisCount: 3, crossAxisCount: 3,
shrinkWrap: true, shrinkWrap: true,

View File

@@ -7,15 +7,13 @@ import 'package:kmobile/data/models/payment_response.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import 'package:confetti/confetti.dart';
class PaymentAnimationScreen extends StatefulWidget { /*class PaymentAnimationScreen extends StatefulWidget {
final Future<PaymentResponse> paymentResponse; final Future<PaymentResponse> paymentResponse;
const PaymentAnimationScreen({ const PaymentAnimationScreen({super.key, required this.paymentResponse});
super.key,
required this.paymentResponse,
});
@override @override
State<PaymentAnimationScreen> createState() => _PaymentAnimationScreenState(); State<PaymentAnimationScreen> createState() => _PaymentAnimationScreenState();
@@ -29,22 +27,26 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
RenderRepaintBoundary boundary = RenderRepaintBoundary boundary =
_shareKey.currentContext!.findRenderObject() as RenderRepaintBoundary; _shareKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage(pixelRatio: 3.0); ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData? byteData = ByteData? byteData = await image.toByteData(
await image.toByteData(format: ui.ImageByteFormat.png); format: ui.ImageByteFormat.png,
);
Uint8List pngBytes = byteData!.buffer.asUint8List(); Uint8List pngBytes = byteData!.buffer.asUint8List();
final tempDir = await getTemporaryDirectory(); final tempDir = await getTemporaryDirectory();
final file = await File('${tempDir.path}/payment_result.png').create(); final file = await File('${tempDir.path}/payment_result.png').create();
await file.writeAsBytes(pngBytes); await file.writeAsBytes(pngBytes);
await Share.shareXFiles([XFile(file.path)], await Share.shareXFiles([
text: '${AppLocalizations.of(context).paymentResult}'); XFile(file.path),
], text: '${AppLocalizations.of(context).paymentResult}');
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
'${AppLocalizations.of(context).failedToShareScreenshot}: $e')), '${AppLocalizations.of(context).failedToShareScreenshot}: $e',
),
),
); );
} }
} }
@@ -93,12 +95,14 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
? Column( ? Column(
children: [ children: [
Text( Text(
AppLocalizations.of(context) AppLocalizations.of(
.paymentSuccessful, context,
style: TextStyle( ).paymentSuccessful,
style: const TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.green), color: Colors.green,
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
if (response.amount != null) if (response.amount != null)
@@ -107,7 +111,8 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontFamily: 'Rubik'), fontFamily: 'Rubik',
),
), ),
if (response.creditedAccount != null) if (response.creditedAccount != null)
Text( Text(
@@ -115,7 +120,8 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontFamily: 'Rubik'), fontFamily: 'Rubik',
),
), ),
if (response.date != null) if (response.date != null)
Text( Text(
@@ -128,10 +134,11 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
children: [ children: [
Text( Text(
AppLocalizations.of(context).paymentFailed, AppLocalizations.of(context).paymentFailed,
style: TextStyle( style: const TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.red), color: Colors.red,
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
if (response.errorMessage != null) if (response.errorMessage != null)
@@ -161,14 +168,18 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
Icons.share_rounded, Icons.share_rounded,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
), ),
label: Text(AppLocalizations.of(context).share, label: Text(
style: AppLocalizations.of(context).share,
TextStyle(color: Theme.of(context).primaryColor)), style: TextStyle(color: Theme.of(context).primaryColor),
),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor: Theme.of(
Theme.of(context).scaffoldBackgroundColor, context,
).scaffoldBackgroundColor,
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 32, vertical: 12), horizontal: 32,
vertical: 12,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: BorderSide( side: BorderSide(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
@@ -179,23 +190,263 @@ class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
textStyle: const TextStyle( textStyle: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black), color: Colors.black,
),
), ),
), ),
ElevatedButton.icon( ElevatedButton.icon(
onPressed: () { onPressed: () {
Navigator.of(context) Navigator.of(
.popUntil((route) => route.isFirst); context,
).popUntil((route) => route.isFirst);
}, },
label: Text(AppLocalizations.of(context).done), label: Text(AppLocalizations.of(context).done),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 45, vertical: 12), horizontal: 45,
vertical: 12,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
), ),
textStyle: const TextStyle( textStyle: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600), fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
],
);
},
),
);
}
}*/
class PaymentAnimationScreen extends StatefulWidget {
final Future<PaymentResponse> paymentResponse;
const PaymentAnimationScreen({super.key, required this.paymentResponse});
@override
State<PaymentAnimationScreen> createState() => _PaymentAnimationScreenState();
}
class _PaymentAnimationScreenState extends State<PaymentAnimationScreen> {
final GlobalKey _shareKey = GlobalKey();
late ConfettiController _confettiController;
@override
void initState() {
super.initState();
_confettiController = ConfettiController(duration: const Duration(seconds: 2));
}
@override
void dispose() {
_confettiController.dispose();
super.dispose();
}
Future<void> _shareScreenshot() async {
try {
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);
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,
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'${AppLocalizations.of(context).failedToShareScreenshot}: $e',
),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<PaymentResponse>(
future: widget.paymentResponse,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: Lottie.asset(
'assets/animations/rupee.json',
width: 200,
height: 200,
repeat: true,
),
);
}
final response = snapshot.data!;
final isSuccess = response.isSuccess;
if (isSuccess) _confettiController.play();
return Stack(
children: [
Align(
alignment: Alignment.topCenter,
child: ConfettiWidget(
confettiController: _confettiController,
blastDirectionality: BlastDirectionality.explosive,
emissionFrequency: 0.2,
numberOfParticles: 40,
gravity: 0.3,
maxBlastForce: 25,
minBlastForce: 10,
shouldLoop: false,
colors: const [
Colors.green,
Colors.blue,
Colors.pink,
Colors.orange,
],
),
),
Center(
child: RepaintBoundary(
key: _shareKey,
child: Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 80),
Lottie.asset(
isSuccess
? 'assets/animations/done.json'
: 'assets/animations/error.json',
width: 200,
height: 200,
repeat: false,
),
const SizedBox(height: 10),
isSuccess
? Column(
children: [
Text(
AppLocalizations.of(context).paymentSuccessful,
style: const TextStyle(
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',
),
),
if (response.creditedAccount != null)
Text(
'${AppLocalizations.of(context).creditedAccount}: ${response.creditedAccount}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
fontFamily: 'Rubik',
),
),
if (response.date != null)
Text(
"Date: ${response.date!.toLocal().toIso8601String()}",
style: const TextStyle(fontSize: 16),
),
],
)
: Column(
children: [
Text(
AppLocalizations.of(context).paymentFailed,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const SizedBox(height: 16),
if (response.errorMessage != null)
Text(
response.errorMessage!,
style: const TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 40),
],
),
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 80,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
onPressed: _shareScreenshot,
icon: Icon(
Icons.share_rounded,
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,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
shape: RoundedRectangleBorder(
side: BorderSide(color: Theme.of(context).primaryColor, width: 1),
borderRadius: BorderRadius.circular(30),
),
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
ElevatedButton.icon(
onPressed: () {
Navigator.of(context).popUntil((route) => route.isFirst);
},
icon: const Icon(Icons.check),
label: Text(AppLocalizations.of(context).done),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 45, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
textStyle: const TextStyle(
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:flutter/material.dart';
import 'package:kmobile/features/fund_transfer/screens/tpin_set_screen.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> { class _TpinOtpScreenState extends State<TpinOtpScreen> {
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode()); final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
final List<TextEditingController> _controllers = final List<TextEditingController> _controllers = List.generate(
List.generate(4, (_) => TextEditingController()); 4,
(_) => TextEditingController(),
);
@override @override
void dispose() { void dispose() {
@@ -42,9 +44,7 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
if (_enteredOtp == '0000') { if (_enteredOtp == '0000') {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (_) => TpinSetScreen()),
builder: (_) => TpinSetScreen(),
),
); );
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@@ -67,8 +67,11 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
child: Column( child: Column(
// mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.lock_outline, Icon(
size: 48, color: theme.colorScheme.primary), Icons.lock_outline,
size: 48,
color: theme.colorScheme.primary,
),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
AppLocalizations.of(context).otpVerification, AppLocalizations.of(context).otpVerification,
@@ -103,7 +106,7 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
decoration: InputDecoration( decoration: InputDecoration(
counterText: '', counterText: '',
filled: true, filled: true,
fillColor: Colors.blue[50], fillColor: Theme.of(context).primaryColorLight,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide( borderSide: BorderSide(
@@ -133,8 +136,10 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
), ),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary, backgroundColor: theme.colorScheme.primary,
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(vertical: 14, horizontal: 28), vertical: 14,
horizontal: 28,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
), ),
@@ -147,7 +152,8 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
// Resend OTP logic here // Resend OTP logic here
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context).otpResent)), content: Text(AppLocalizations.of(context).otpResent),
),
); );
}, },
child: Text(AppLocalizations.of(context).resendOtp), 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:flutter/material.dart';
import 'package:kmobile/features/fund_transfer/screens/tpin_otp_screen.dart'; import 'package:kmobile/features/fund_transfer/screens/tpin_otp_screen.dart';
@@ -10,16 +10,17 @@ class TpinSetupPromptScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(title: Text(AppLocalizations.of(context).setTpin)),
title: Text(AppLocalizations.of(context).setTpin),
),
body: Padding( body: Padding(
padding: const EdgeInsets.symmetric(vertical: 100.0), padding: const EdgeInsets.symmetric(vertical: 100.0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.lock_person_rounded, Icon(
size: 60, color: theme.colorScheme.primary), Icons.lock_person_rounded,
size: 60,
color: theme.colorScheme.primary,
),
const SizedBox(height: 18), const SizedBox(height: 18),
Text( Text(
AppLocalizations.of(context).tpinRequired, AppLocalizations.of(context).tpinRequired,
@@ -45,8 +46,10 @@ class TpinSetupPromptScreen extends StatelessWidget {
), ),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primary, backgroundColor: theme.colorScheme.primary,
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(vertical: 14, horizontal: 32), vertical: 14,
horizontal: 32,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
@@ -54,9 +57,7 @@ class TpinSetupPromptScreen extends StatelessWidget {
onPressed: () { onPressed: () {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (_) => const TpinOtpScreen()),
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:flutter/material.dart';
import 'package:kmobile/api/services/auth_service.dart'; import 'package:kmobile/api/services/auth_service.dart';
@@ -66,14 +66,17 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
shape: shape: RoundedRectangleBorder(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)), borderRadius: BorderRadius.circular(18),
),
title: Column( title: Column(
children: [ children: [
Icon(Icons.check_circle, color: Colors.green, size: 60), Icon(Icons.check_circle, color: Colors.green, size: 60),
SizedBox(height: 12), SizedBox(height: 12),
Text(AppLocalizations.of(context).success, Text(
style: TextStyle(fontWeight: FontWeight.bold)), AppLocalizations.of(context).success,
style: TextStyle(fontWeight: FontWeight.bold),
),
], ],
), ),
content: Text( content: Text(
@@ -85,8 +88,10 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
onPressed: () { onPressed: () {
Navigator.of(ctx).pop(); Navigator.of(ctx).pop();
}, },
child: Text(AppLocalizations.of(context).ok, child: Text(
style: TextStyle(fontSize: 16)), AppLocalizations.of(context).ok,
style: TextStyle(fontSize: 16),
),
), ),
], ],
), ),
@@ -125,7 +130,7 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
['1', '2', '3'], ['1', '2', '3'],
['4', '5', '6'], ['4', '5', '6'],
['7', '8', '9'], ['7', '8', '9'],
['Enter', '0', '<'] ['Enter', '0', '<'],
]; ];
return Column( return Column(
@@ -144,8 +149,9 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
_handleComplete(); _handleComplete();
} else { } else {
setState(() { setState(() {
_errorText = _errorText = AppLocalizations.of(
AppLocalizations.of(context).pleaseEnter6Digits; context,
).pleaseEnter6Digits;
}); });
} }
} else if (key.isNotEmpty) { } else if (key.isNotEmpty) {
@@ -166,7 +172,7 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
key == '<' ? '' : key, key == '<' ? '' : key,
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
color: key == 'Enter' ? Colors.blue : Colors.black, color: key == 'Enter' ? Theme.of(context).primaryColor : Colors.black,
), ),
), ),
), ),
@@ -195,7 +201,7 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
child: Column( child: Column(
children: [ children: [
const Spacer(), const Spacer(),
const Icon(Icons.lock_outline, size: 60, color: Colors.blue), Icon(Icons.lock_outline, size: 60, color: Theme.of(context).primaryColor),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
getTitle(), getTitle(),
@@ -206,8 +212,10 @@ class _TpinSetScreenState extends State<TpinSetScreen> {
if (_errorText != null) if (_errorText != null)
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: Text(_errorText!, child: Text(
style: const TextStyle(color: Colors.red)), _errorText!,
style: const TextStyle(color: Colors.red),
),
), ),
const Spacer(), const Spacer(),
buildNumberPad(), buildNumberPad(),

View File

@@ -1,4 +1,6 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; // ignore_for_file: unused_field
import '../../../l10n/app_localizations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:kmobile/api/services/auth_service.dart'; import 'package:kmobile/api/services/auth_service.dart';
@@ -7,7 +9,6 @@ import 'package:kmobile/data/models/transfer.dart';
import 'package:kmobile/di/injection.dart'; import 'package:kmobile/di/injection.dart';
import 'package:kmobile/features/fund_transfer/screens/payment_animation.dart'; import 'package:kmobile/features/fund_transfer/screens/payment_animation.dart';
import 'package:kmobile/features/fund_transfer/screens/tpin_prompt_screen.dart'; import 'package:kmobile/features/fund_transfer/screens/tpin_prompt_screen.dart';
import 'package:kmobile/features/fund_transfer/screens/transaction_success_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
class TransactionPinScreen extends StatefulWidget { class TransactionPinScreen extends StatefulWidget {
@@ -36,9 +37,7 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
if (!isSet && mounted) { if (!isSet && mounted) {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (_) => const TpinSetupPromptScreen()),
builder: (_) => const TpinSetupPromptScreen(),
),
); );
} else if (mounted) { } else if (mounted) {
setState(() => _loading = false); setState(() => _loading = false);
@@ -48,7 +47,8 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
setState(() => _loading = false); setState(() => _loading = false);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context).tpinStatusFailed)), content: Text(AppLocalizations.of(context).tpinStatusFailed),
),
); );
} }
} }
@@ -74,8 +74,8 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
height: 20, height: 20,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all(color: Colors.blue, width: 2), border: Border.all(color: Theme.of(context).primaryColor, width: 2),
color: index < _pin.length ? Colors.blue : Colors.transparent, color: index < _pin.length ? Theme.of(context).primaryColor : Colors.transparent,
), ),
); );
}), }),
@@ -95,8 +95,8 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: content: Text(AppLocalizations.of(context).enter6DigitTpin),
Text(AppLocalizations.of(context).enter6DigitTpin)), ),
); );
} }
} else { } else {
@@ -122,11 +122,13 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
Row(children: [_buildKey('1'), _buildKey('2'), _buildKey('3')]), Row(children: [_buildKey('1'), _buildKey('2'), _buildKey('3')]),
Row(children: [_buildKey('4'), _buildKey('5'), _buildKey('6')]), Row(children: [_buildKey('4'), _buildKey('5'), _buildKey('6')]),
Row(children: [_buildKey('7'), _buildKey('8'), _buildKey('9')]), Row(children: [_buildKey('7'), _buildKey('8'), _buildKey('9')]),
Row(children: [ Row(
children: [
_buildKey('done', icon: Icons.check), _buildKey('done', icon: Icons.check),
_buildKey('0'), _buildKey('0'),
_buildKey('back', icon: Icons.backspace_outlined), _buildKey('back', icon: Icons.backspace_outlined),
]), ],
),
], ],
); );
} }
@@ -136,19 +138,21 @@ class _TransactionPinScreen extends State<TransactionPinScreen> {
final transfer = widget.transactionData; final transfer = widget.transactionData;
transfer.tpin = _pin.join(); transfer.tpin = _pin.join();
try { try {
final paymentResponse = final paymentResponse = paymentService.processQuickPayWithinBank(
paymentService.processQuickPayWithinBank(transfer); transfer,
);
Navigator.of(context).pushReplacement( Navigator.of(context).pushReplacement(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => builder: (_) =>
PaymentAnimationScreen(paymentResponse: paymentResponse)), PaymentAnimationScreen(paymentResponse: paymentResponse),
),
); );
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text(e.toString())), 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:io';
import 'dart:typed_data'; import 'dart:typed_data';
@@ -35,8 +35,9 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String transactionDate = final String transactionDate = DateTime.now().toLocal().toString().split(
DateTime.now().toLocal().toString().split(' ')[0]; ' ',
)[0];
final String creditAccount = widget.creditAccount; final String creditAccount = widget.creditAccount;
return Scaffold( return Scaffold(
@@ -49,40 +50,27 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const CircleAvatar( CircleAvatar(
radius: 50, radius: 50,
backgroundColor: Colors.blue, backgroundColor: Theme.of(context).primaryColor,
child: Icon( child: Icon(Icons.check, color: Theme.of(context).scaffoldBackgroundColor, size: 60),
Icons.check,
color: Colors.white,
size: 60,
),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
AppLocalizations.of(context).transactionSuccess, AppLocalizations.of(context).transactionSuccess,
style: TextStyle( style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
fontSize: 18,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
"On $transactionDate", "On $transactionDate",
style: const TextStyle( style: const TextStyle(fontSize: 14, color: Colors.black54),
fontSize: 14,
color: Colors.black54,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
"${AppLocalizations.of(context).toAccountNumber}: $creditAccount", "${AppLocalizations.of(context).toAccountNumber}: $creditAccount",
style: const TextStyle( style: const TextStyle(fontSize: 12, color: Colors.black87),
fontSize: 12,
color: Colors.black87,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@@ -104,11 +92,11 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.white, backgroundColor: Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.blueAccent, foregroundColor: Theme.of(context).primaryColorLight,
side: side: const BorderSide(color: Colors.black, width: 1),
const BorderSide(color: Colors.black, width: 1), elevation: 0,
elevation: 0), ),
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
@@ -119,14 +107,15 @@ class _TransactionSuccessScreen extends State<TransactionSuccessScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => const NavigationScaffold(),
const NavigationScaffold())); ),
);
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.blue[900], backgroundColor: Theme.of(context).primaryColorDark,
foregroundColor: Colors.white, foregroundColor: Theme.of(context).scaffoldBackgroundColor,
), ),
child: Text(AppLocalizations.of(context).done), child: Text(AppLocalizations.of(context).done),
), ),

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kmobile/config/theme_type.dart';
import 'package:kmobile/features/auth/controllers/theme_cubit.dart';
import '../../../l10n/app_localizations.dart';
class ColorThemeDialog extends StatelessWidget {
const ColorThemeDialog({super.key});
@override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text(AppLocalizations.of(context).selectThemeColor),
children: [
ListTile(
leading: const CircleAvatar(backgroundColor: Colors.deepPurple),
title: Text(AppLocalizations.of(context).violet),
onTap: () {
context.read<ThemeCubit>().changeTheme(ThemeType.violet);
Navigator.pop(context);
},
),
// ListTile(
// leading: const CircleAvatar(backgroundColor: Colors.green),
// title: const Text('Green'),
// onTap: () {
// context.read<ThemeCubit>().changeTheme(ThemeType.green);
// Navigator.pop(context);
// },
// ),
// ListTile(
// leading: const CircleAvatar(backgroundColor: Colors.orange),
// title: const Text('Orange'),
// onTap: () {
// context.read<ThemeCubit>().changeTheme(ThemeType.orange);
// Navigator.pop(context);
// },
// ),
ListTile(
leading: const CircleAvatar(backgroundColor: Colors.blue),
title: Text(AppLocalizations.of(context).blue),
onTap: () {
context.read<ThemeCubit>().changeTheme(ThemeType.blue);
Navigator.pop(context);
},
),
],
);
}
}

View File

@@ -1,52 +1,37 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import 'package:kmobile/app.dart'; import 'package:kmobile/app.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LanguageDialog extends StatelessWidget { class LanguageDialog extends StatelessWidget {
const LanguageDialog({super.key}); const LanguageDialog({Key? key}) : super(key: key);
String getLocaleName(AppLocalizations localizations, String code) { Future<void> _setLocale(BuildContext context, String langCode) async {
final localeCodeMap = { final prefs = await SharedPreferences.getInstance();
'en': localizations.english, await prefs.setString('locale', langCode); // Save selected language
'hi': localizations.hindi, KMobile.setLocale(context, Locale(langCode)); // Update locale in app
}; Navigator.of(context).pop(); // Close the dialog
return localeCodeMap[code] ?? 'Unknown';
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Builder(
builder: (context) {
final localizations = AppLocalizations.of(context);
final supportedLocales = [
const Locale('en'),
const Locale('hi'),
];
return AlertDialog( return AlertDialog(
title: Text(localizations.language), title: Text(AppLocalizations.of(context).selectLanguage),
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: supportedLocales.map((locale) { children: [
return ListTile( ListTile(
leading: const Icon(Icons.language), leading: const Icon(Icons.language),
title: Text(getLocaleName(localizations, locale.languageCode)), title: const Text('English'),
onTap: () { onTap: () => _setLocale(context, 'en'),
Navigator.pop(context);
KMobile.setLocale(context, locale);
},
);
}).toList(),
), ),
actions: [ ListTile(
TextButton( leading: const Icon(Icons.language),
onPressed: () => Navigator.pop(context), title: const Text('हिन्दी'),
child: Text(localizations.cancel), onTap: () => _setLocale(context, 'hi'),
), ),
], ],
); ),
},
); );
} }
} }

View File

@@ -1,6 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'language_dialog.dart'; import 'language_dialog.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'color_theme_dialog.dart';
import '../../../l10n/app_localizations.dart';
import 'package:kmobile/features/auth/controllers/theme_cubit.dart';
import 'package:kmobile/features/auth/controllers/theme_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class PreferenceScreen extends StatelessWidget { class PreferenceScreen extends StatelessWidget {
const PreferenceScreen({super.key}); const PreferenceScreen({super.key});
@@ -11,21 +15,48 @@ class PreferenceScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(loc.preferences), // Localized "Preferences" title: Text(loc.preferences),
), ),
body: ListView( body: BlocBuilder<ThemeCubit, ThemeState>(
builder: (context, state) {
return ListView(
children: [ children: [
// Theme Mode Switch (Light/Dark)
// ListTile(
// leading: const Icon(Icons.brightness_6),
// title: const Text("Theme Mode"),
// trailing: Switch(
// value: state.isDarkMode,
// onChanged: (val) {
// context.read<ThemeCubit>().toggleDarkMode(val);
// },
// ),
// ),
//Color_Theme_Selection
ListTile( ListTile(
leading: const Icon(Icons.language), leading: const Icon(Icons.color_lens),
title: Text(loc.language), // Localized "Language" title: Text(AppLocalizations.of(context).themeColor),
onTap: () { onTap: () {
showDialog( showDialog(
context: context, context: context,
builder: (context) => LanguageDialog(), builder: (_) => const ColorThemeDialog(),
);
}
),
// Language Selection
ListTile(
leading: const Icon(Icons.language),
title: Text(loc.language),
onTap: () {
showDialog(
context: context,
builder: (_) => const LanguageDialog(), // your custom language dialog
); );
}, },
), ),
], ],
);
},
), ),
); );
} }

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../l10n/app_localizations.dart';
import 'package:kmobile/features/profile/preferences/preference_screen.dart'; import 'package:kmobile/features/profile/preferences/preference_screen.dart';
class ProfileScreen extends StatelessWidget { class ProfileScreen extends StatelessWidget {
@@ -21,9 +21,7 @@ class ProfileScreen extends StatelessWidget {
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (context) => const PreferenceScreen()),
builder: (_) => const PreferenceScreen(),
),
); );
}, },
), ),

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:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
@@ -109,8 +109,10 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
Text( Text(
widget.debitAccount, widget.debitAccount,
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w500), fontSize: 18,
) fontWeight: FontWeight.w500,
),
),
], ],
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
@@ -120,7 +122,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -150,7 +152,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -177,7 +179,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -202,7 +204,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -226,7 +228,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -253,7 +255,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -269,7 +271,8 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
} }
return null; return null;
}, },
)), ),
),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
@@ -279,7 +282,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -287,14 +290,15 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
borderSide: BorderSide(color: Colors.black, width: 2), borderSide: BorderSide(color: Colors.black, width: 2),
), ),
), ),
items: [ items:
[
AppLocalizations.of(context).savings, AppLocalizations.of(context).savings,
AppLocalizations.of(context).current AppLocalizations.of(context).current,
] ]
.map((e) => DropdownMenuItem( .map(
value: e, (e) =>
child: Text(e), DropdownMenuItem(value: e, child: Text(e)),
)) )
.toList(), .toList(),
onChanged: (value) => setState(() { onChanged: (value) => setState(() {
accountType = value!; accountType = value!;
@@ -316,7 +320,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -338,7 +342,7 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
border: OutlineInputBorder(), border: OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
@@ -366,8 +370,10 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
const SizedBox(height: 30), const SizedBox(height: 30),
Row( Row(
children: [ children: [
Text(AppLocalizations.of(context).transactionMode, Text(
style: TextStyle(fontWeight: FontWeight.w500)), AppLocalizations.of(context).transactionMode,
style: TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded(child: buildTransactionModeSelector()), Expanded(child: buildTransactionModeSelector()),
], ],
@@ -376,28 +382,27 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: SwipeButton.expand( child: SwipeButton.expand(
thumb: const Icon( thumb: Icon(Icons.arrow_forward, color: Theme.of(context).scaffoldBackgroundColor),
Icons.arrow_forward, activeThumbColor: Theme.of(context).primaryColorDark,
color: Colors.white, activeTrackColor: Theme.of(context).primaryColorLight,
),
activeThumbColor: Colors.blue[900],
activeTrackColor: Colors.blue.shade100,
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
height: 56, height: 56,
child: Text( child: Text(
AppLocalizations.of(context).swipeToPay, AppLocalizations.of(context).swipeToPay,
style: style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
), ),
onSwipe: () { onSwipe: () {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
// Perform payment logic // Perform payment logic
final selectedMode = final selectedMode = transactionModes(
transactionModes(context)[selectedTransactionIndex]; context,
)[selectedTransactionIndex];
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
'${AppLocalizations.of(context).payingVia} $selectedMode...')), '${AppLocalizations.of(context).payingVia} $selectedMode...',
),
),
); );
// Navigator.push( // Navigator.push(
// context, // context,
@@ -409,7 +414,8 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
// ))); // )));
} }
}, },
)), ),
),
], ],
), ),
), ),
@@ -432,19 +438,17 @@ class _QuickPayOutsideBankScreen extends State<QuickPayOutsideBankScreen> {
margin: const EdgeInsets.symmetric(horizontal: 4), margin: const EdgeInsets.symmetric(horizontal: 4),
padding: const EdgeInsets.symmetric(vertical: 5), padding: const EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected ? Colors.blue[200] : Colors.white, color: isSelected ? Theme.of(context).primaryColor : Theme.of(context).scaffoldBackgroundColor,
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
border: Border.all( border: Border.all(
color: isSelected ? Colors.blue : Colors.grey, color: isSelected ? Theme.of(context).primaryColor : Theme.of(context).scaffoldBackgroundColor,
width: isSelected ? 0 : 1.2, width: isSelected ? 0 : 1.2,
), ),
), ),
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
transactionModes(context)[index], transactionModes(context)[index],
style: const TextStyle( style: const TextStyle(color: Colors.black),
color: Colors.black,
),
), ),
), ),
), ),

View File

@@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:kmobile/features/quick_pay/screens/quick_pay_outside_bank_screen.dart'; import 'package:kmobile/features/quick_pay/screens/quick_pay_outside_bank_screen.dart';
import 'package:kmobile/features/quick_pay/screens/quick_pay_within_bank_screen.dart'; import 'package:kmobile/features/quick_pay/screens/quick_pay_within_bank_screen.dart';
import 'package:material_symbols_icons/material_symbols_icons.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 QuickPayScreen extends StatefulWidget { class QuickPayScreen extends StatefulWidget {
final String debitAccount; final String debitAccount;
@@ -25,8 +25,9 @@ class _QuickPayScreen extends State<QuickPayScreen> {
}, },
), ),
title: Text( title: Text(
AppLocalizations.of(context).quickPay, AppLocalizations.of(context).quickPay.replaceAll('\n', ' '),
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), style:
const TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
), ),
centerTitle: false, centerTitle: false,
actions: [ actions: [
@@ -55,14 +56,15 @@ class _QuickPayScreen extends State<QuickPayScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => QuickPayWithinBankScreen( builder: (context) => QuickPayWithinBankScreen(
debitAccount: widget.debitAccount))); debitAccount: widget.debitAccount,
),
),
);
}, },
), ),
const Divider( const Divider(height: 1),
height: 1,
),
QuickPayManagementTile( QuickPayManagementTile(
//disable: true, disable: true,
icon: Symbols.output_circle, icon: Symbols.output_circle,
label: AppLocalizations.of(context).outsideBank, label: AppLocalizations.of(context).outsideBank,
onTap: () { onTap: () {
@@ -70,12 +72,13 @@ class _QuickPayScreen extends State<QuickPayScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => QuickPayOutsideBankScreen( builder: (context) => QuickPayOutsideBankScreen(
debitAccount: widget.debitAccount))); debitAccount: widget.debitAccount,
),
),
);
}, },
), ),
const Divider( const Divider(height: 1),
height: 1,
),
], ],
), ),
); );

View File

@@ -1,10 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:flutter_swipe_button/flutter_swipe_button.dart'; import 'package:flutter_swipe_button/flutter_swipe_button.dart';
import 'package:kmobile/api/services/beneficiary_service.dart';
import 'package:kmobile/data/models/transfer.dart'; import 'package:kmobile/data/models/transfer.dart';
import 'package:kmobile/di/injection.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import '../../fund_transfer/screens/transaction_pin_screen.dart'; import '../../fund_transfer/screens/transaction_pin_screen.dart';
//import 'package:flutter_neumorphic/flutter_neumorphic.dart';
class QuickPayWithinBankScreen extends StatefulWidget { class QuickPayWithinBankScreen extends StatefulWidget {
final String debitAccount; final String debitAccount;
@@ -23,6 +26,64 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
final TextEditingController amountController = TextEditingController(); final TextEditingController amountController = TextEditingController();
String? _selectedAccountType; String? _selectedAccountType;
String? _beneficiaryName;
bool _isValidating = false;
bool _isBeneficiaryValidated = false;
String? _validationError;
@override
void initState() {
super.initState();
accountNumberController.addListener(_resetBeneficiaryValidation);
confirmAccountNumberController.addListener(_resetBeneficiaryValidation);
}
void _resetBeneficiaryValidation() {
if (_isBeneficiaryValidated ||
_beneficiaryName != null ||
_validationError != null) {
setState(() {
_isBeneficiaryValidated = false;
_beneficiaryName = null;
_validationError = null;
});
}
}
@override
void dispose() {
accountNumberController.removeListener(_resetBeneficiaryValidation);
confirmAccountNumberController.removeListener(_resetBeneficiaryValidation);
accountNumberController.dispose();
confirmAccountNumberController.dispose();
amountController.dispose();
super.dispose();
}
Future<void> _validateBeneficiary() async {
var beneficiaryService = getIt<BeneficiaryService>();
setState(() {
_isValidating = true;
_validationError = null;
});
try {
final name = await beneficiaryService
.validateBeneficiaryWithinBank(accountNumberController.text);
setState(() {
_beneficiaryName = name;
_isBeneficiaryValidated = true;
_isValidating = false;
});
} catch (e) {
setState(() {
_validationError = "Account Number not from KCCB";
_isValidating = false;
_isBeneficiaryValidated = false;
_beneficiaryName = null;
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -35,7 +96,8 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
), ),
title: Text( title: Text(
AppLocalizations.of(context).quickPayOwnBank, AppLocalizations.of(context).quickPayOwnBank,
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500), style:
const TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
), ),
centerTitle: false, centerTitle: false,
actions: [ actions: [
@@ -64,10 +126,10 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
TextFormField( TextFormField(
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).debitAccountNumber, labelText: AppLocalizations.of(context).debitAccountNumber,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
), ),
readOnly: true, readOnly: true,
controller: TextEditingController(text: widget.debitAccount), controller: TextEditingController(text: widget.debitAccount),
@@ -79,14 +141,14 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
TextFormField( TextFormField(
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).accountNumber, labelText: AppLocalizations.of(context).accountNumber,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2), borderSide: BorderSide(color: Colors.black, width: 2),
), ),
), ),
@@ -118,14 +180,14 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).confirmAccountNumber, labelText: AppLocalizations.of(context).confirmAccountNumber,
// prefixIcon: Icon(Icons.person), // prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2), borderSide: BorderSide(color: Colors.black, width: 2),
), ),
), ),
@@ -141,19 +203,73 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
return null; return null;
}, },
), ),
if (!_isBeneficiaryValidated)
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isValidating
? null
: () {
if (accountNumberController.text.length == 11 &&
confirmAccountNumberController.text ==
accountNumberController.text) {
_validateBeneficiary();
} else {
setState(() {
_validationError =
'Please enter a valid and matching account number.';
});
}
},
child: _isValidating
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Validate Beneficiary'),
),
),
),
if (_beneficiaryName != null && _isBeneficiaryValidated)
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Row(
children: [
const Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
Text(
'Beneficiary: $_beneficiaryName',
style: const TextStyle(
color: Colors.green, fontWeight: FontWeight.bold),
),
],
),
),
if (_validationError != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
_validationError!,
style: const TextStyle(color: Colors.red),
),
),
const SizedBox(height: 24), const SizedBox(height: 24),
DropdownButtonFormField<String>( DropdownButtonFormField<String>(
decoration: InputDecoration( decoration: InputDecoration(
labelText: labelText: AppLocalizations.of(
AppLocalizations.of(context).beneficiaryAccountType, context,
border: OutlineInputBorder(), ).beneficiaryAccountType,
border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2), borderSide: BorderSide(color: Colors.black, width: 2),
), ),
), ),
@@ -193,14 +309,14 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
TextFormField( TextFormField(
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).amount, labelText: AppLocalizations.of(context).amount,
border: OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).scaffoldBackgroundColor,
enabledBorder: OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 2), borderSide: BorderSide(color: Colors.black, width: 2),
), ),
), ),
@@ -222,21 +338,26 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: SwipeButton.expand( child: SwipeButton.expand(
thumb: const Icon( thumb: Icon(Icons.arrow_forward, color: Theme.of(context).dialogBackgroundColor),
Icons.arrow_forward,
color: Colors.white,
),
activeThumbColor: Theme.of(context).primaryColor, activeThumbColor: Theme.of(context).primaryColor,
activeTrackColor: activeTrackColor: Theme.of(
Theme.of(context).colorScheme.secondary.withAlpha(100), context,
).colorScheme.secondary.withAlpha(100),
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
height: 56, height: 56,
child: Text( child: Text(
AppLocalizations.of(context).swipeToPay, AppLocalizations.of(context).swipeToPay,
style: TextStyle(fontSize: 16), style: const TextStyle(fontSize: 16),
), ),
onSwipe: () { onSwipe: () {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
if (!_isBeneficiaryValidated) {
setState(() {
_validationError =
'Please validate beneficiary before proceeding.';
});
return;
}
// Perform payment logic // Perform payment logic
Navigator.push( Navigator.push(
context, context,
@@ -248,11 +369,61 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
toAccountType: _selectedAccountType!, toAccountType: _selectedAccountType!,
amount: amountController.text, amount: amountController.text,
), ),
))); ),
),
);
} }
}, },
), ),
), ),
/*Align(
alignment: Alignment.center,
child: NeumorphicButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
if (!_isBeneficiaryValidated) {
setState(() {
_validationError =
'Please validate beneficiary before proceeding.';
});
return;
}
// Perform payment logic
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TransactionPinScreen(
transactionData: Transfer(
fromAccount: widget.debitAccount,
toAccount: accountNumberController.text,
toAccountType: _selectedAccountType!,
amount: amountController.text,
),
),
),
);
}
},
style: NeumorphicStyle(
color: Theme.of(context).primaryColor,
depth: 4,
intensity: 0.8,
boxShape: NeumorphicBoxShape.roundRect(BorderRadius.circular(30)),
),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Center(
child: Text(
AppLocalizations.of(context).swipeToPay,
style: const TextStyle(
fontSize: 16,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),*/
// SliderButton( // SliderButton(
// action: () async { // action: () async {
// ///Do something here OnSlide // ///Do something here OnSlide
@@ -290,7 +461,7 @@ class _QuickPayWithinBankScreen extends State<QuickPayWithinBankScreen> {
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
fillColor: Colors.white, fillColor: Theme.of(context).dialogBackgroundColor,
enabledBorder: const OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),

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:flutter/material.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
@@ -38,7 +38,8 @@ class _ServiceScreen extends State<ServiceScreen> {
}, },
child: const CircleAvatar( child: const CircleAvatar(
backgroundImage: AssetImage( backgroundImage: AssetImage(
'assets/images/avatar.jpg'), // Replace with your image 'assets/images/avatar.jpg',
), // Replace with your image
radius: 20, radius: 20,
), ),
), ),
@@ -52,33 +53,25 @@ class _ServiceScreen extends State<ServiceScreen> {
label: AppLocalizations.of(context).accountOpeningDeposit, label: AppLocalizations.of(context).accountOpeningDeposit,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ServiceManagementTile( ServiceManagementTile(
icon: Symbols.add, icon: Symbols.add,
label: AppLocalizations.of(context).accountOpeningLoan, label: AppLocalizations.of(context).accountOpeningLoan,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ServiceManagementTile( ServiceManagementTile(
icon: Symbols.captive_portal, icon: Symbols.captive_portal,
label: AppLocalizations.of(context).quickLinks, label: AppLocalizations.of(context).quickLinks,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
),
ServiceManagementTile( ServiceManagementTile(
icon: Symbols.missing_controller, icon: Symbols.missing_controller,
label: AppLocalizations.of(context).branchLocator, label: AppLocalizations.of(context).branchLocator,
onTap: () {}, onTap: () {},
), ),
const Divider( const Divider(height: 1),
height: 1,
)
], ],
), ),
); );

View File

@@ -219,6 +219,16 @@
"setMPIN": "Set your mPIN", "setMPIN": "Set your mPIN",
"confirmMPIN": "Confirm your mPIN", "confirmMPIN": "Confirm your mPIN",
"kconnect": "Kconnect", "kconnect": "Kconnect",
"kccBankFull": "Kangra Central Co-operative Bank" "kccBankFull": "Kangra Central Co-operative Bank",
"themeColor": "Theme Color",
"selectThemeColor": "Select Theme Color",
"violet": "Violet",
"blue": "Blue",
"invalidIfsc": "Invalid IFSC code",
"validIfsc": "Valid IFSC",
"beneficiaryAddedSuccess": "Beneficiary Added Successfully",
"beneficiaryAdditionFailed": "Beneficiary Addition Failed",
"noBeneficiaryFound": "No beneficiaries found",
"beneficiaryName": "Beneficiary Name"
} }

View File

@@ -218,6 +218,16 @@
"enterMPIN": "अपना mPIN दर्ज करें", "enterMPIN": "अपना mPIN दर्ज करें",
"setMPIN": "अपना mPIN सेट करें", "setMPIN": "अपना mPIN सेट करें",
"confirmMPIN": "अपना mPIN की पुष्टि करें", "confirmMPIN": "अपना mPIN की पुष्टि करें",
"kconnect": "केकनेक्ट", "kconnect": "के-कनेक्ट",
"kccBankFull": "कांगड़ा सेंट्रल को-ऑपरेटिव बैंक" "kccBankFull": "कांगड़ा सेंट्रल को-ऑपरेटिव बैंक",
"themeColor": "थीम रंग",
"selectThemeColor": "थीम रंग चुनें",
"violet": "बैंगनी",
"blue": "नीला",
"invalidIfsc": "अमान्य IFSC कोड",
"validIfsc": "मान्य IFSC",
"beneficiaryAddedSuccess": "लाभार्थी सफलतापूर्वक जोड़ा गया",
"beneficiaryAdditionFailed": "लाभार्थी जोड़ने में विफल",
"noBeneficiaryFound": "कोई लाभार्थी नहीं मिला",
"beneficiaryName": "लाभार्थी नाम"
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,664 @@
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get profile => 'Profile';
@override
String get preferences => 'Preferences';
@override
String get language => 'Language';
@override
String get selectLanguage => 'Select Language';
@override
String get english => 'English';
@override
String get hindi => 'Hindi';
@override
String get cancel => 'Cancel';
@override
String get home => 'Home';
@override
String get card => 'Card';
@override
String get services => 'Services';
@override
String get quickPay => 'Quick \n Pay';
@override
String get quickLinks => 'Quick Links';
@override
String get recentTransactions => 'Recent Transactions';
@override
String get accountNumber => 'Account Number';
@override
String get enableBiometric => 'Enable Biometric Authentication';
@override
String get useBiometricPrompt => 'Use fingerprint/face ID for faster login?';
@override
String get later => 'Later';
@override
String get enable => 'Enable';
@override
String get noTransactions => 'No transactions found for this account.';
@override
String get somethingWentWrong => 'Something went wrong';
@override
String failedToLoad(Object error) {
return 'Failed to load transactions: $error';
}
@override
String get failedToRefresh => 'Failed to refresh data';
@override
String get hi => 'Hi';
@override
String get kMobile => 'kMobile';
@override
String get scanBiometric => 'Scan to enable Biometric login';
@override
String get savingsAccount => 'Savings Account';
@override
String get loanAccount => 'Loan Account';
@override
String get termDeposit => 'Term Deposit Account';
@override
String get recurringDeposit => 'Recurring Deposit Account';
@override
String get unknownAccount => 'Unknown Account Type';
@override
String get customerInfo => 'Customer \n Info';
@override
String get fundTransfer => 'Fund Transfer';
@override
String get accountInfo => 'Account Info';
@override
String get accountStatement => 'Account Statement';
@override
String get handleCheque => 'Handle \n Cheque';
@override
String get manageBeneficiary => 'Manage \n Beneficiary';
@override
String get contactUs => 'Contact \n Us';
@override
String get addBeneficiary => 'Add Beneficiary';
@override
String get confirmAccountNumber => 'Confirm Account Number';
@override
String get name => 'Name';
@override
String get ifscCode => 'IFSC Code';
@override
String get bankName => 'Beneficiary Bank Name';
@override
String get branchName => 'Branch Name';
@override
String get accountType => 'Account Type';
@override
String get savings => 'Savings';
@override
String get current => 'Current';
@override
String get phone => 'Phone';
@override
String get validateAndAdd => 'Validate and Add';
@override
String get beneficiaryAdded => 'Beneficiary Added Successfully';
@override
String get invalidIfscFormat => 'Invalid IFSC Format';
@override
String get noIfscDetails => 'No details found for IFSC';
@override
String get enterValidAccountNumber => 'Enter a valid account number';
@override
String get reenterAccountNumber => 'Re-enter Account Number';
@override
String get accountMismatch => 'Account Numbers do not match';
@override
String get nameRequired => 'Name is required';
@override
String get enterIfsc => 'Enter IFSC code';
@override
String get enterValidPhone => 'Enter a valid phone number';
@override
String get payNow => 'Pay Now';
@override
String get beneficiaries => 'Beneficiaries';
@override
String get cif => 'CIF';
@override
String get activeAccounts => 'Number of Active Accounts';
@override
String get mobileNumber => 'Mobile Number';
@override
String get dateOfBirth => 'Date of Birth';
@override
String get branchCode => 'Branch Code';
@override
String get branchAddress => 'Branch Address';
@override
String get primaryId => 'Primary ID';
@override
String get quickPayOwnBank => 'Quick Pay - Own Bank';
@override
String get debitAccountNumber => 'Debit Account Number';
@override
String get accountNumberRequired => 'Account Number is required';
@override
String get validAccountNumber => 'Enter a valid account number';
@override
String get beneficiaryAccountType => 'Beneficiary Account Type';
@override
String get loan => 'Loan';
@override
String get selectAccountType => 'Please select account type';
@override
String get amount => 'Amount';
@override
String get amountRequired => 'Amount is required';
@override
String get validAmount => 'Enter a valid amount';
@override
String get swipeToPay => 'Swipe to Pay';
@override
String get outsideBank => 'Outside Bank';
@override
String get ownBank => 'Own Bank';
@override
String get neft => 'NEFT';
@override
String get rtgs => 'RTGS';
@override
String get imps => 'IMPS';
@override
String get quickPayOutsideBank => 'Quick Pay - Outside Bank';
@override
String get debitFrom => 'Debit from';
@override
String get bankNameRequired => 'Beneficiary Bank name is required';
@override
String get branchNameRequired => 'Beneficiary Branch Name is required';
@override
String get ifscRequired => 'IFSC Code is required';
@override
String get phoneRequired => 'Phone number is required';
@override
String get transactionMode => 'Transaction Mode';
@override
String get payingVia => 'Paying via';
@override
String get accountOpeningDeposit => 'Account Opening Request - Deposit';
@override
String get accountOpeningLoan => 'Account Opening Request - Loan';
@override
String get branchLocator => 'Branch Locator';
@override
String get emailLaunchError => 'Could not launch email client for';
@override
String get dialerLaunchError => 'Could not launch dialer for';
@override
String get writeToUs => 'Write to us';
@override
String get keyContacts => 'Key Contacts';
@override
String get chairman => 'Chairman';
@override
String get managingDirector => 'Managing Director';
@override
String get gmWest => 'General Manager (West)';
@override
String get gmNorth => 'General Manager (North)';
@override
String get enquiry => 'Enquiry';
@override
String get fundTransferBeneficiary => 'Fund Transfer - Beneficiary';
@override
String get enterAmount => 'Enter Amount';
@override
String get customerNumber => 'Customer Number';
@override
String get productName => 'Product Name';
@override
String get accountStatus => 'Account Status';
@override
String get availableBalance => 'Available Balance';
@override
String get currentBalance => 'Current Balance';
@override
String get filters => 'Filters';
@override
String get fromDate => 'From Date';
@override
String get toDate => 'To Date';
@override
String get minAmount => 'Min Amount';
@override
String get maxAmount => 'Max amount';
@override
String get lastTenTransactions => 'Showing last 10 transactions';
@override
String get applyDebitCard => 'Apply Debit Card';
@override
String get blockUnblockCard => 'Block / Unblock Card';
@override
String get changeCardPin => 'Change Card PIN';
@override
String get cardNumber => 'Card Number';
@override
String get cvv => 'CVV';
@override
String get expiryDate => 'Expiry Date';
@override
String get next => 'Next';
@override
String get block => 'Block';
@override
String get approvedAmount => 'Approved Amount';
@override
String get failedToLoadTransactions => 'Failed to load transactions';
@override
String get pleaseSelectDateFirst => 'Please select date first';
@override
String get cardBlocked => 'Card has been blocked';
@override
String get blockCard => 'Block Card';
@override
String get enterValidCardNumber => 'Enter valid card number';
@override
String get cvv3Digits => 'CVV must be 3 digits';
@override
String get selectExpiryDate => 'Select expiry date';
@override
String get cardManagement => 'Card Management';
@override
String get paymentResult => 'Payment Result';
@override
String get failedToShareScreenshot => 'Failed to share screenshot';
@override
String get paymentSuccessful => 'Payment successful';
@override
String get cardDetails => 'Card Details';
@override
String get cardPin => 'Card PIN';
@override
String get amountEntered => 'Amount entered';
@override
String get enterNewPin => 'Enter new PIN';
@override
String get pleaseEnterNewPin => 'Please enter new PIN';
@override
String get pin4Digits => 'PIN must be at least 4 digits';
@override
String get enterAgain => 'Enter Again';
@override
String get pinsDoNotMatch => 'PINs do not match. Try again.';
@override
String get submit => 'Submit';
@override
String get chequeManagement => 'Cheque Management';
@override
String get requestChequeBook => 'Request Chequebook';
@override
String get chequeDeposit => 'Cheque Deposit';
@override
String get stopCheque => 'Stop Cheque';
@override
String get revokeStop => 'Revoke Stop';
@override
String get positivePay => 'Positive Pay';
@override
String get pinSetSuccess => 'PIN set successfully';
@override
String get creditedAccount => 'Credited Account';
@override
String get date => 'Date';
@override
String get paymentFailed => 'Payment Failed';
@override
String get share => 'Share';
@override
String get done => 'Done';
@override
String get invalidOtp => 'Invalid OTP';
@override
String get enterOtp => 'Enter OTP';
@override
String get otpVerification => 'OTP Verification';
@override
String get otpSentMessage => 'Enter the 4-digit OTP sent to your mobile number';
@override
String get verifyOtp => 'Verify OTP';
@override
String get otpResent => 'OTP Resent';
@override
String get resendOtp => 'Resend OTP';
@override
String get setTpin => 'Set TPIN';
@override
String get tpinRequired => 'TPIN Required';
@override
String get tpinRequiredMessage => 'You need to set your TPIN to continue with secure transactions';
@override
String get setTpinTitle => 'Set TPIN';
@override
String get tpinInfo => 'Your TPIN is a 6-digit code used to authorize transactions. Keep it safe and do not share it with anyone.';
@override
String get tpinFailed => 'Failed to set TPIN. Please try again.';
@override
String get success => 'Success!';
@override
String get tpinSetSuccess => 'Your TPIN was set successfully';
@override
String get ok => 'OK';
@override
String get pinsMismatchRetry => 'PINs do not match. Try again.';
@override
String get pleaseEnter6Digits => 'Please enter 6 digits';
@override
String get setNewTpin => 'Set your new TPIN';
@override
String get confirmNewTpin => 'Confirm your new TPIN';
@override
String get tpinStatusFailed => 'Failed to check TPIN status';
@override
String get enter6DigitTpin => 'Please enter a 6-digit TPIN';
@override
String get tpin => 'TPIN';
@override
String get enterTpin => 'Enter your TPIN';
@override
String get transactionSuccess => 'Transaction Successful';
@override
String get on => 'On';
@override
String get toAccountNumber => 'To Account Number';
@override
String get shareText => 'Share';
@override
String get enableFingerprintLogin => 'Enable Fingerprint Login?';
@override
String get enableFingerprintMessage => 'Would you like to enable fingerprint authentication for faster login?';
@override
String get no => 'No';
@override
String get yes => 'Yes';
@override
String get authenticateToEnable => 'Authenticate to enable fingerprint login';
@override
String get exitApp => 'Exit App';
@override
String get exitConfirmation => 'Do you really want to exit?';
@override
String get loading => 'Loading......';
@override
String get enableFingerprintQuick => 'Enable fingerprint authentication for quick login?';
@override
String get kccb => 'KCCB';
@override
String get password => 'Password';
@override
String get pleaseEnterUsername => 'Please enter your username';
@override
String get pleaseEnterPassword => 'Please enter your password';
@override
String get login => 'Login';
@override
String get or => 'OR';
@override
String get register => 'Register';
@override
String get authenticateToAccess => 'Authenticate to access kmobile';
@override
String get incorrectMPIN => 'Incorrect mPIN. Try again.';
@override
String get pleaseEnter4Digits => 'Please enter 4 digits.';
@override
String get enterMPIN => 'Enter your mPIN';
@override
String get setMPIN => 'Set your mPIN';
@override
String get confirmMPIN => 'Confirm your mPIN';
@override
String get kconnect => 'Kconnect';
@override
String get kccBankFull => 'Kangra Central Co-operative Bank';
@override
String get themeColor => 'Theme Color';
@override
String get selectThemeColor => 'Select Theme Color';
@override
String get violet => 'Violet';
@override
String get blue => 'Blue';
@override
String get invalidIfsc => 'Invalid IFSC code';
@override
String get validIfsc => 'Valid IFSC';
@override
String get beneficiaryAddedSuccess => 'Beneficiary Added Successfully';
@override
String get beneficiaryAdditionFailed => 'Beneficiary Addition Failed';
@override
String get noBeneficiaryFound => 'No beneficiaries found';
@override
String get beneficiaryName => 'Beneficiary Name';
}

View File

@@ -0,0 +1,664 @@
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for Hindi (`hi`).
class AppLocalizationsHi extends AppLocalizations {
AppLocalizationsHi([String locale = 'hi']) : super(locale);
@override
String get profile => 'प्रोफ़ाइल';
@override
String get preferences => 'वरीयताएँ';
@override
String get language => 'भाषा';
@override
String get selectLanguage => 'भाषा चुनिए';
@override
String get english => 'अंग्रेज़ी';
@override
String get hindi => 'हिंदी';
@override
String get cancel => 'रद्द करें';
@override
String get home => 'होम';
@override
String get card => 'कार्ड';
@override
String get services => 'सेवाएं';
@override
String get quickPay => 'क्विक \n पे';
@override
String get quickLinks => 'त्वरित लिंक';
@override
String get recentTransactions => 'हाल की लेनदेन';
@override
String get accountNumber => 'खाता संख्या';
@override
String get enableBiometric => 'बायोमेट्रिक प्रमाणीकरण सक्षम करें';
@override
String get useBiometricPrompt => 'तेज़ लॉगिन के लिए फिंगरप्रिंट/फेस आईडी का उपयोग करें?';
@override
String get later => 'बाद में';
@override
String get enable => 'सक्षम करें';
@override
String get noTransactions => 'इस खाते के लिए कोई लेनदेन नहीं मिला।';
@override
String get somethingWentWrong => 'कुछ गलत हो गया';
@override
String failedToLoad(Object error) {
return 'लेनदेन लोड करने में विफल: $error';
}
@override
String get failedToRefresh => 'डेटा रिफ्रेश करने में विफल';
@override
String get hi => 'नमस्ते';
@override
String get kMobile => 'के मोबाइल';
@override
String get scanBiometric => 'बायोमेट्रिक लॉगिन सक्षम करने के लिए स्कैन करें';
@override
String get savingsAccount => 'बचत खाता';
@override
String get loanAccount => 'ऋण खाता';
@override
String get termDeposit => 'मियादी जमा खाता';
@override
String get recurringDeposit => 'आवर्ती जमा खाता';
@override
String get unknownAccount => 'अज्ञात खाता प्रकार';
@override
String get customerInfo => 'ग्राहक \n जानकारी';
@override
String get fundTransfer => 'फंड ट्रांसफर';
@override
String get accountInfo => 'खाता जानकारी';
@override
String get accountStatement => 'खाता विवरण';
@override
String get handleCheque => 'चेक \n संभालें';
@override
String get manageBeneficiary => 'लाभार्थी \n प्रबंधन';
@override
String get contactUs => 'संपर्क \n करें';
@override
String get addBeneficiary => 'लाभार्थी जोड़ें';
@override
String get confirmAccountNumber => 'खाता संख्या की पुष्टि करें';
@override
String get name => 'नाम';
@override
String get ifscCode => 'आईएफ़एससी कोड';
@override
String get bankName => 'लाभार्थी बैंक का नाम';
@override
String get branchName => 'शाखा का नाम';
@override
String get accountType => 'खाते प्रकार';
@override
String get savings => 'बचत';
@override
String get current => 'चालू';
@override
String get phone => 'फ़ोन';
@override
String get validateAndAdd => 'सत्यापित करें और जोड़ें';
@override
String get beneficiaryAdded => 'लाभार्थी सफलतापूर्वक जोड़ा गया';
@override
String get invalidIfscFormat => 'अमान्य IFSC प्रारूप';
@override
String get noIfscDetails => 'इस IFSC के लिए कोई विवरण नहीं मिला';
@override
String get enterValidAccountNumber => 'कृपया एक मान्य खाता संख्या दर्ज करें';
@override
String get reenterAccountNumber => 'फिर से खाता संख्या दर्ज करें';
@override
String get accountMismatch => 'खाता संख्याएँ मेल नहीं खा रही हैं';
@override
String get nameRequired => 'नाम आवश्यक है';
@override
String get enterIfsc => 'IFSC कोड दर्ज करें';
@override
String get enterValidPhone => 'कृपया एक मान्य फोन नंबर दर्ज करें';
@override
String get payNow => 'अब भुगतान करें';
@override
String get beneficiaries => 'लाभार्थी';
@override
String get cif => 'सीआईएफ';
@override
String get activeAccounts => 'सक्रिय खातों की संख्या';
@override
String get mobileNumber => 'मोबाइल नंबर';
@override
String get dateOfBirth => 'जन्म तिथि';
@override
String get branchCode => 'शाखा कोड';
@override
String get branchAddress => 'शाखा पता';
@override
String get primaryId => 'प्राथमिक पहचान';
@override
String get quickPayOwnBank => 'क्विक पे - स्वयं का बैंक';
@override
String get debitAccountNumber => 'डेबिट खाता संख्या';
@override
String get accountNumberRequired => 'खाता संख्या आवश्यक है';
@override
String get validAccountNumber => 'एक मान्य खाता संख्या दर्ज करें';
@override
String get beneficiaryAccountType => 'लाभार्थी खाता प्रकार';
@override
String get loan => 'ऋण';
@override
String get selectAccountType => 'कृपया खाता प्रकार चुनें';
@override
String get amount => 'राशि';
@override
String get amountRequired => 'राशि आवश्यक है';
@override
String get validAmount => 'एक मान्य राशि दर्ज करें';
@override
String get swipeToPay => 'भुगतान करने के लिए स्वाइप करें';
@override
String get outsideBank => 'बाहरी बैंक';
@override
String get ownBank => 'स्वयं का बैंक';
@override
String get neft => 'एनईएफटी';
@override
String get rtgs => 'आरटीजीएस';
@override
String get imps => 'आईएमपीएस';
@override
String get quickPayOutsideBank => 'त्वरित भुगतान - बाहरी बैंक';
@override
String get debitFrom => 'से डेबिट करें';
@override
String get bankNameRequired => 'बैंक का नाम आवश्यक है';
@override
String get branchNameRequired => 'शाखा का नाम आवश्यक है';
@override
String get ifscRequired => 'आईएफएससी कोड आवश्यक है';
@override
String get phoneRequired => 'फ़ोन नंबर आवश्यक है';
@override
String get transactionMode => 'लेन-देन का माध्यम';
@override
String get payingVia => 'के माध्यम से भुगतान';
@override
String get accountOpeningDeposit => 'खाता खोलने का अनुरोध - जमा';
@override
String get accountOpeningLoan => 'खाता खोलने का अनुरोध - ऋण';
@override
String get branchLocator => 'शाखा लोकेटर';
@override
String get emailLaunchError => 'ईमेल क्लाइंट खोलने में विफल: ';
@override
String get dialerLaunchError => 'डायलर खोलने में विफल: ';
@override
String get writeToUs => 'हमें लिखें';
@override
String get keyContacts => 'मुख्य संपर्क';
@override
String get chairman => 'अध्यक्ष';
@override
String get managingDirector => 'प्रबंध निदेशक';
@override
String get gmWest => 'महाप्रबंधक (पश्चिम)';
@override
String get gmNorth => 'महाप्रबंधक (उत्तर)';
@override
String get enquiry => 'पूछताछ';
@override
String get fundTransferBeneficiary => 'फंड ट्रांसफर - लाभार्थी';
@override
String get enterAmount => 'राशि दर्ज करें';
@override
String get customerNumber => 'ग्राहक संख्या';
@override
String get productName => 'उत्पाद का नाम';
@override
String get accountStatus => 'खाता स्थिति';
@override
String get availableBalance => 'उपलब्ध शेष राशि';
@override
String get currentBalance => 'वर्तमान शेष राशि';
@override
String get filters => 'फ़िल्टर';
@override
String get fromDate => 'प्रारंभ तिथि';
@override
String get toDate => 'अंतिम तिथि';
@override
String get minAmount => 'न्यूनतम राशि';
@override
String get maxAmount => 'अधिकतम राशि';
@override
String get lastTenTransactions => 'अंतिम 10 लेनदेन प्रदर्शित किए जा रहे हैं';
@override
String get applyDebitCard => 'डेबिट कार्ड के लिए आवेदन करें';
@override
String get blockUnblockCard => 'कार्ड ब्लॉक/अनब्लॉक करें';
@override
String get changeCardPin => 'कार्ड पिन बदलें';
@override
String get cardNumber => 'कार्ड संख्या';
@override
String get cvv => 'सीवीवी';
@override
String get expiryDate => 'समाप्ति तिथि';
@override
String get next => 'आगे';
@override
String get block => 'ब्लॉक करें';
@override
String get approvedAmount => 'स्वीकृत राशि';
@override
String get failedToLoadTransactions => 'लेन-देन लोड करने में विफल';
@override
String get pleaseSelectDateFirst => 'कृपया पहले तिथि चुनें';
@override
String get cardBlocked => 'कार्ड ब्लॉक कर दिया गया है';
@override
String get blockCard => 'कार्ड ब्लॉक करें';
@override
String get enterValidCardNumber => 'मान्य कार्ड नंबर दर्ज करें';
@override
String get cvv3Digits => 'सीवीवी 3 अंकों का होना चाहिए';
@override
String get selectExpiryDate => 'समाप्ति तिथि चुनें';
@override
String get cardManagement => 'कार्ड प्रबंधन';
@override
String get paymentResult => 'भुगतान परिणाम';
@override
String get failedToShareScreenshot => 'स्क्रीनशॉट साझा करने में विफल';
@override
String get paymentSuccessful => 'भुगतान सफल';
@override
String get cardDetails => 'कार्ड विवरण';
@override
String get cardPin => 'कार्ड पिन';
@override
String get amountEntered => 'दर्ज की गई राशि';
@override
String get enterNewPin => 'नया पिन दर्ज करें';
@override
String get pleaseEnterNewPin => 'कृपया नया पिन दर्ज करें';
@override
String get pin4Digits => 'पिन कम से कम 4 अंकों का होना चाहिए';
@override
String get enterAgain => 'पुनः दर्ज करें';
@override
String get pinsDoNotMatch => 'PIN मेल नहीं खा रहे हैं। पुनः प्रयास करें।';
@override
String get submit => 'जमा करें';
@override
String get chequeManagement => 'चेक प्रबंधन';
@override
String get requestChequeBook => 'चेकबुक का अनुरोध करें';
@override
String get chequeDeposit => 'चेक जमा';
@override
String get stopCheque => 'चेक रोकें';
@override
String get revokeStop => 'रोक हटाएं';
@override
String get positivePay => 'पॉजिटिव पे';
@override
String get pinSetSuccess => 'पिन सफलतापूर्वक सेट किया गया';
@override
String get creditedAccount => 'क्रेडिटेड खाता';
@override
String get date => 'तिथि';
@override
String get paymentFailed => 'भुगतान विफल';
@override
String get share => 'साझा करें';
@override
String get done => 'पूर्ण';
@override
String get invalidOtp => 'अमान्य ओटीपी';
@override
String get enterOtp => 'ओटीपी दर्ज करें';
@override
String get otpVerification => 'ओटीपी सत्यापन';
@override
String get otpSentMessage => 'अपने मोबाइल नंबर पर भेजा गया 4-अंकों का ओटीपी दर्ज करें';
@override
String get verifyOtp => 'ओटीपी सत्यापित करें';
@override
String get otpResent => 'ओटीपी पुनः भेजा गया';
@override
String get resendOtp => 'ओटीपी पुनः भेजें';
@override
String get setTpin => 'टी-पिन सेट करें';
@override
String get tpinRequired => 'टी-पिन आवश्यक है';
@override
String get tpinRequiredMessage => 'सुरक्षित लेनदेन के लिए टी-पिन सेट करना आवश्यक है';
@override
String get setTpinTitle => 'टी-पिन सेट करें';
@override
String get tpinInfo => 'आपका टी-पिन 6 अंकों का कोड है जिसका उपयोग लेन-देन को प्रमाणित करने के लिए किया जाता है। इसे सुरक्षित रखें और किसी से साझा न करें।';
@override
String get tpinFailed => 'टी-पिन सेट करने में विफल। कृपया पुनः प्रयास करें।';
@override
String get success => 'सफलता!';
@override
String get tpinSetSuccess => 'आपका टी-पिन सफलतापूर्वक सेट हो गया है';
@override
String get ok => 'ठीक है';
@override
String get pinsMismatchRetry => 'पिन मेल नहीं खाते। पुनः प्रयास करें।';
@override
String get pleaseEnter6Digits => 'कृपया 6 अंक दर्ज करें';
@override
String get setNewTpin => 'अपना नया टी-पिन सेट करें';
@override
String get confirmNewTpin => 'अपना नया टी-पिन पुष्टि करें';
@override
String get tpinStatusFailed => 'टी-पिन स्थिति की जांच करने में विफल';
@override
String get enter6DigitTpin => 'कृपया 6 अंकों का टी-पिन दर्ज करें';
@override
String get tpin => 'टी-पिन';
@override
String get enterTpin => 'अपना टी-पिन दर्ज करें';
@override
String get transactionSuccess => 'लेन-देन सफल रहा';
@override
String get on => 'पर';
@override
String get toAccountNumber => 'खाते संख्या में';
@override
String get shareText => 'साझा करें';
@override
String get enableFingerprintLogin => 'फिंगरप्रिंट लॉगिन सक्षम करें?';
@override
String get enableFingerprintMessage => 'क्या आप तेज लॉगिन के लिए फिंगरप्रिंट प्रमाणीकरण सक्षम करना चाहेंगे?';
@override
String get no => 'नहीं';
@override
String get yes => 'हाँ';
@override
String get authenticateToEnable => 'फिंगरप्रिंट लॉगिन सक्षम करने के लिए प्रमाणीकरण करें';
@override
String get exitApp => 'ऐप बंद करें';
@override
String get exitConfirmation => 'क्या आप वाकई ऐप से बाहर निकलना चाहते हैं?';
@override
String get loading => 'लोड हो रहा है......';
@override
String get enableFingerprintQuick => 'तेज़ लॉगिन के लिए फिंगरप्रिंट प्रमाणीकरण सक्षम करें?';
@override
String get kccb => 'केसीसीबी';
@override
String get password => 'पासवर्ड';
@override
String get pleaseEnterUsername => 'कृपया उपयोगकर्ता नाम दर्ज करें';
@override
String get pleaseEnterPassword => 'कृपया पासवर्ड दर्ज करें';
@override
String get login => 'लॉगिन';
@override
String get or => 'या';
@override
String get register => 'रजिस्टर करें';
@override
String get authenticateToAccess => 'kmobile तक पहुंच के लिए प्रमाणीकरण करें';
@override
String get incorrectMPIN => 'गलत mPIN. कृपया पुनः प्रयास करें।';
@override
String get pleaseEnter4Digits => 'कृपया 4 अंक दर्ज करें।';
@override
String get enterMPIN => 'अपना mPIN दर्ज करें';
@override
String get setMPIN => 'अपना mPIN सेट करें';
@override
String get confirmMPIN => 'अपना mPIN की पुष्टि करें';
@override
String get kconnect => 'के-कनेक्ट';
@override
String get kccBankFull => 'कांगड़ा सेंट्रल को-ऑपरेटिव बैंक';
@override
String get themeColor => 'थीम रंग';
@override
String get selectThemeColor => 'थीम रंग चुनें';
@override
String get violet => 'बैंगनी';
@override
String get blue => 'नीला';
@override
String get invalidIfsc => 'अमान्य IFSC कोड';
@override
String get validIfsc => 'मान्य IFSC';
@override
String get beneficiaryAddedSuccess => 'लाभार्थी सफलतापूर्वक जोड़ा गया';
@override
String get beneficiaryAdditionFailed => 'लाभार्थी जोड़ने में विफल';
@override
String get noBeneficiaryFound => 'कोई लाभार्थी नहीं मिला';
@override
String get beneficiaryName => 'लाभार्थी नाम';
}

View File

@@ -14,6 +14,5 @@ void main() async {
// Initialize dependencies // Initialize dependencies
await setupDependencies(); await setupDependencies();
runApp(const KMobile()); runApp(const KMobile());
} }

View File

@@ -89,6 +89,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.18.0"
confetti:
dependency: "direct main"
description:
name: confetti
sha256: "979aafde2428c53947892c95eb244466c109c129b7eee9011f0a66caaca52267"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@@ -203,6 +211,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_neumorphic:
dependency: "direct main"
description:
name: flutter_neumorphic
sha256: "02606d937a3ceaa497b8a7c25f3efa95188bf93d77ebf0bd6552e432db4c2ec6"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@@ -289,10 +305,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: get_it name: get_it
sha256: f126a3e286b7f5b578bf436d5592968706c4c1de28a228b870ce375d9f743103 sha256: e87cd1d108e472a0580348a543a0c49ed3d70c8a5c809c6d418583e595d0a389
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.0.3" version: "8.1.0"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@@ -457,10 +473,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: material_symbols_icons name: material_symbols_icons
sha256: "7c50901b39d1ad645ee25d920aed008061e1fd541a897b4ebf2c01d966dbf16b" sha256: ef20d86fb34c2b59eb7553c4d795bb8a7ec8c890c53ffd3148c64f7adc46ae50
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2815.1" version: "4.2858.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:

View File

@@ -30,6 +30,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_neumorphic : 3.2.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
@@ -57,6 +58,7 @@ dependencies:
shimmer: ^3.0.0 shimmer: ^3.0.0
lottie: ^2.6.0 lottie: ^2.6.0
share_plus: ^7.2.1 share_plus: ^7.2.1
confetti: ^0.7.0