IFSC Code Validation and Add Beneficiary Validation API integration
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:kmobile/data/models/ifsc.dart';
|
||||||
|
import 'package:kmobile/data/models/beneficiary.dart';
|
||||||
|
|
||||||
class BeneficiaryService {
|
class BeneficiaryService {
|
||||||
final Dio _dio;
|
final Dio _dio;
|
||||||
@@ -22,4 +24,84 @@ class BeneficiaryService {
|
|||||||
throw Exception('Unexpected error: ${e.toString()}');
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
51
lib/app.dart
51
lib/app.dart
@@ -69,48 +69,6 @@ class _KMobileState extends State<KMobile> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @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>()),
|
|
||||||
BlocProvider<ThemeCubit>(create: (_) => getIt<ThemeCubit>()),
|
|
||||||
],
|
|
||||||
child: MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
locale: _locale,
|
|
||||||
supportedLocales: const [
|
|
||||||
Locale('en'),
|
|
||||||
Locale('hi'),
|
|
||||||
],
|
|
||||||
localizationsDelegates: const [
|
|
||||||
AppLocalizations.delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
title: 'kMobile',
|
|
||||||
|
|
||||||
// Dynamic Theme and ThemeMode
|
|
||||||
theme: AppThemes.getLightTheme(_themeController.currentTheme),
|
|
||||||
darkTheme: AppThemes.getDarkTheme(_themeController.currentTheme),
|
|
||||||
themeMode: _themeModeController.currentThemeMode,
|
|
||||||
|
|
||||||
onGenerateRoute: AppRoutes.generateRoute,
|
|
||||||
initialRoute: AppRoutes.splash,
|
|
||||||
home: showSplash ? const SplashScreen() : const AuthGate(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Set status bar color and brightness
|
// Set status bar color and brightness
|
||||||
@@ -128,6 +86,8 @@ BlocProvider<ThemeCubit>(create: (_) => getIt<ThemeCubit>()),
|
|||||||
],
|
],
|
||||||
child: BlocBuilder<ThemeCubit, ThemeState>(
|
child: BlocBuilder<ThemeCubit, ThemeState>(
|
||||||
builder: (context, themeState) {
|
builder: (context, themeState) {
|
||||||
|
print('global theme state changed');
|
||||||
|
print(themeState);
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
locale: _locale ?? const Locale('en'),
|
locale: _locale ?? const Locale('en'),
|
||||||
@@ -142,11 +102,10 @@ GlobalWidgetsLocalizations.delegate,
|
|||||||
GlobalCupertinoLocalizations.delegate,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
],
|
],
|
||||||
title: 'kMobile',
|
title: 'kMobile',
|
||||||
//theme: themeState.lightTheme,
|
|
||||||
//theme: AppThemes.getLightTheme(themeState.themeType),
|
//theme: AppThemes.getLightTheme(themeState.themeType),
|
||||||
theme: themeState.lightTheme,
|
theme: themeState.getThemeData(),
|
||||||
darkTheme: AppThemes.getDarkTheme(themeState.themeType),
|
// darkTheme: AppThemes.getDarkTheme(themeState.themeType),
|
||||||
themeMode: themeState.themeMode,
|
themeMode: ThemeMode.system,
|
||||||
onGenerateRoute: AppRoutes.generateRoute,
|
onGenerateRoute: AppRoutes.generateRoute,
|
||||||
initialRoute: AppRoutes.splash,
|
initialRoute: AppRoutes.splash,
|
||||||
home: showSplash ? const SplashScreen() : const AuthGate(),
|
home: showSplash ? const SplashScreen() : const AuthGate(),
|
||||||
|
37
lib/data/models/beneficiary.dart
Normal file
37
lib/data/models/beneficiary.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
class Beneficiary {
|
||||||
|
final String accountNo;
|
||||||
|
final String accountType;
|
||||||
|
final String name;
|
||||||
|
final String ifscCode;
|
||||||
|
|
||||||
|
|
||||||
|
Beneficiary({
|
||||||
|
required this.accountNo,
|
||||||
|
required this.accountType,
|
||||||
|
required this.name,
|
||||||
|
required this.ifscCode,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Beneficiary.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Beneficiary(
|
||||||
|
accountNo: json['accountNo'] ?? '',
|
||||||
|
accountType: json['accountType'] ?? '',
|
||||||
|
name: json['name'] ?? '',
|
||||||
|
ifscCode: json['ifscCode'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'accountNo': accountNo,
|
||||||
|
'accountType': accountType,
|
||||||
|
'name': name,
|
||||||
|
'ifscCode' : ifscCode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Beneficiary(accountNo: $accountNo, accountType: $accountType, ifscCode: $ifscCode, name: $name)';
|
||||||
|
}
|
||||||
|
}
|
32
lib/data/models/ifsc.dart
Normal file
32
lib/data/models/ifsc.dart
Normal 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)';
|
||||||
|
}
|
||||||
|
}
|
@@ -18,7 +18,7 @@ Future<void> setupDependencies() async {
|
|||||||
|
|
||||||
//getIt.registerLazySingleton<ThemeController>(() => ThemeController());
|
//getIt.registerLazySingleton<ThemeController>(() => ThemeController());
|
||||||
//getIt.registerLazySingleton<ThemeModeController>(() => ThemeModeController());
|
//getIt.registerLazySingleton<ThemeModeController>(() => ThemeModeController());
|
||||||
getIt.registerLazySingleton<ThemeCubit>(() => ThemeCubit());
|
getIt.registerSingleton<ThemeCubit>( ThemeCubit());
|
||||||
|
|
||||||
// Register Dio client
|
// Register Dio client
|
||||||
getIt.registerSingleton<Dio>(_createDioClient());
|
getIt.registerSingleton<Dio>(_createDioClient());
|
||||||
@@ -58,7 +58,8 @@ 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: {
|
||||||
|
@@ -250,9 +250,9 @@ 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,
|
radius: 12,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Shimmer.fromColors(
|
title: Shimmer.fromColors(
|
||||||
@@ -261,7 +261,7 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
height: 10,
|
height: 10,
|
||||||
width: 100,
|
width: 100,
|
||||||
color: Colors.white,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Shimmer.fromColors(
|
subtitle: Shimmer.fromColors(
|
||||||
@@ -270,7 +270,7 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
height: 8,
|
height: 8,
|
||||||
width: 60,
|
width: 60,
|
||||||
color: Colors.white,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -1,50 +1,60 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'theme_state.dart';
|
import 'theme_state.dart';
|
||||||
import 'package:kmobile/config/theme_type.dart';
|
import 'package:kmobile/config/theme_type.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:kmobile/config/themes.dart';
|
// import 'package:kmobile/config/themes.dart';
|
||||||
|
|
||||||
class ThemeCubit extends Cubit<ThemeState> {
|
class ThemeCubit extends Cubit<ThemeState> {
|
||||||
ThemeCubit()
|
ThemeCubit(): super(ThemeViolet()) {
|
||||||
: super(ThemeState(
|
|
||||||
lightTheme: AppThemes.getLightTheme(ThemeType.violet),
|
|
||||||
themeMode: ThemeMode.light,
|
|
||||||
themeType: ThemeType.violet,
|
|
||||||
)) {
|
|
||||||
loadTheme();
|
loadTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadTheme() async {
|
Future<void> loadTheme() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final themeIndex = prefs.getInt('theme_type') ?? 0;
|
final themeIndex = prefs.getInt('theme_type') ?? 0;
|
||||||
final isDark = prefs.getBool('is_dark_mode') ?? false;
|
// final isDark = prefs.getBool('is_dark_mode') ?? false;
|
||||||
|
|
||||||
final type = ThemeType.values[themeIndex];
|
final type = ThemeType.values[themeIndex];
|
||||||
emit(state.copyWith(
|
switch(type) {
|
||||||
lightTheme: AppThemes.getLightTheme(type),
|
case ThemeType.blue:
|
||||||
themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
|
emit(ThemeBlue());
|
||||||
themeType: type,
|
case ThemeType.violet:
|
||||||
));
|
emit(ThemeViolet());
|
||||||
|
default:
|
||||||
|
emit(ThemeViolet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeTheme(ThemeType type) async {
|
Future<void> changeTheme(ThemeType type) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setInt('theme_type', type.index);
|
await prefs.setInt('theme_type', type.index);
|
||||||
|
log("Mode Change");
|
||||||
emit(state.copyWith(
|
print("mode changed");
|
||||||
lightTheme: AppThemes.getLightTheme(type),
|
switch(type) {
|
||||||
themeType: type,
|
case ThemeType.blue:
|
||||||
));
|
emit(ThemeBlue());
|
||||||
}
|
print('blue matched');
|
||||||
|
break;
|
||||||
Future<void> toggleDarkMode(bool isDark) async {
|
case ThemeType.violet:
|
||||||
final prefs = await SharedPreferences.getInstance();
|
emit(ThemeViolet());
|
||||||
await prefs.setBool('is_dark_mode', isDark);
|
print('violet matched');
|
||||||
|
break;
|
||||||
emit(state.copyWith(
|
default:
|
||||||
themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
|
emit(ThemeBlue());
|
||||||
));
|
print('default macthed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Future<void> toggleDarkMode(bool isDark) async {
|
||||||
|
// final prefs = await SharedPreferences.getInstance();
|
||||||
|
// await prefs.setBool('is_dark_mode', isDark);
|
||||||
|
|
||||||
|
// emit(state.copyWith(
|
||||||
|
// themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1,27 +1,52 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kmobile/config/theme_type.dart';
|
import 'package:kmobile/config/theme_type.dart';
|
||||||
|
import 'package:kmobile/config/themes.dart';
|
||||||
|
|
||||||
class ThemeState {
|
// class ThemeState {
|
||||||
final ThemeData lightTheme;
|
// final ThemeData lightTheme;
|
||||||
final ThemeMode themeMode;
|
// final ThemeMode themeMode;
|
||||||
final ThemeType themeType;
|
// final ThemeType themeType;
|
||||||
|
|
||||||
ThemeState({
|
// ThemeState({
|
||||||
required this.lightTheme,
|
// required this.lightTheme,
|
||||||
required this.themeMode,
|
// required this.themeMode,
|
||||||
required this.themeType,
|
// required this.themeType,
|
||||||
});
|
// });
|
||||||
|
|
||||||
ThemeState copyWith({
|
// ThemeState copyWith({
|
||||||
ThemeData? lightTheme,
|
// ThemeData? lightTheme,
|
||||||
ThemeMode? themeMode,
|
// ThemeMode? themeMode,
|
||||||
ThemeType? themeType,
|
// ThemeType? themeType,
|
||||||
}) {
|
// }) {
|
||||||
return ThemeState(
|
// return ThemeState(
|
||||||
lightTheme: lightTheme ?? this.lightTheme,
|
// lightTheme: lightTheme ?? this.lightTheme,
|
||||||
themeMode: themeMode ?? this.themeMode,
|
// themeMode: themeMode ?? this.themeMode,
|
||||||
themeType: themeType ?? this.themeType,
|
// themeType: themeType ?? this.themeType,
|
||||||
);
|
// );
|
||||||
|
// }
|
||||||
|
// bool get isDarkMode => themeMode == ThemeMode.dark;
|
||||||
|
// }
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
bool get isDarkMode => themeMode == ThemeMode.dark;
|
|
||||||
}
|
}
|
@@ -1,6 +1,11 @@
|
|||||||
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 'beneficiary_result_page.dart';
|
||||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
|
import '../../../di/injection.dart';
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AddBeneficiaryScreen extends StatefulWidget {
|
class AddBeneficiaryScreen extends StatefulWidget {
|
||||||
@@ -22,6 +27,8 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
|||||||
final TextEditingController ifscController = TextEditingController();
|
final TextEditingController ifscController = TextEditingController();
|
||||||
final TextEditingController phoneController = TextEditingController();
|
final TextEditingController phoneController = TextEditingController();
|
||||||
|
|
||||||
|
|
||||||
|
bool _isLoading2 = false;
|
||||||
late String accountType;
|
late String accountType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -34,7 +41,7 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _submitForm() {
|
/*void _submitForm() {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
// Handle successful submission
|
// Handle successful submission
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -56,11 +63,11 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Navigate to Payment Screen or do something
|
// Navigate to Payment Screen or do something
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(foregroundColor: Colors.blue[200]),
|
style: TextButton.styleFrom(foregroundColor: Theme.of(context).primaryColorLight),
|
||||||
child: Text(AppLocalizations.of(context).payNow),
|
child: Text(AppLocalizations.of(context).payNow),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.close, color: Colors.white),
|
icon: Icon(Icons.close, color: Theme.of(context).scaffoldBackgroundColor),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||||
},
|
},
|
||||||
@@ -70,58 +77,82 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
ifsc? _ifscData;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
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(
|
||||||
|
const SnackBar(content: Text('Invalid IFSC code')),
|
||||||
|
);
|
||||||
bankNameController.clear();
|
bankNameController.clear();
|
||||||
branchNameController.clear();
|
branchNameController.clear();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text(AppLocalizations.of(context).invalidIfscFormat)),
|
|
||||||
);
|
|
||||||
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)) {
|
|
||||||
final data = mockIfscData[ifsc]!;
|
|
||||||
bankNameController.text = data['bank']!;
|
|
||||||
branchNameController.text = data['branch']!;
|
|
||||||
} else {
|
} else {
|
||||||
bankNameController.clear();
|
print("Valid IFSC: ${result.bankName}, ${result.branchName}");
|
||||||
branchNameController.clear();
|
bankNameController.text = result.bankName;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
branchNameController.text = result.branchName;
|
||||||
SnackBar(content: Text(AppLocalizations.of(context).noIfscDetails)),
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
🔸 Optional: Use real IFSC API like:
|
String _selectedAccountType = 'Savings'; // default value
|
||||||
final response = await http.get(Uri.parse('https://ifsc.razorpay.com/$ifsc'));
|
|
||||||
if (response.statusCode == 200) {
|
void validateAndAddBeneficiary() async {
|
||||||
final data = jsonDecode(response.body);
|
setState(() {
|
||||||
bankNameController.text = data['BANK'];
|
_isLoading = true;
|
||||||
branchNameController.text = data['BRANCH'];
|
});
|
||||||
|
|
||||||
|
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.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => BeneficiaryResultPage(isSuccess: isFound),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
*/
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text("Something went wrong during validation.")),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading2 = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -263,110 +294,7 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
|||||||
? AppLocalizations.of(context).nameRequired
|
? AppLocalizations.of(context).nameRequired
|
||||||
: null,
|
: 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),
|
const SizedBox(height: 24),
|
||||||
// 🔹 IFSC Code Field
|
// 🔹 IFSC Code Field
|
||||||
TextFormField(
|
TextFormField(
|
||||||
@@ -538,7 +466,8 @@ class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 250,
|
width: 250,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: _submitForm,
|
onPressed: _isLoading2 ? null :
|
||||||
|
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),
|
||||||
|
@@ -0,0 +1,19 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BeneficiaryResultPage extends StatelessWidget {
|
||||||
|
final bool isSuccess;
|
||||||
|
|
||||||
|
const BeneficiaryResultPage({super.key, required this.isSuccess});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Text(
|
||||||
|
isSuccess ? 'Beneficiary Added Successfully!' : 'Beneficiary Addition Failed!',
|
||||||
|
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -482,7 +482,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
const FundTransferBeneficiaryScreen()));
|
const FundTransferBeneficiaryScreen()));
|
||||||
}, disable: true),
|
}, disable: false),
|
||||||
_buildQuickLink(Symbols.server_person,
|
_buildQuickLink(Symbols.server_person,
|
||||||
AppLocalizations.of(context).accountInfo, () {
|
AppLocalizations.of(context).accountInfo, () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@@ -518,7 +518,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
const ManageBeneficiariesScreen()));
|
const ManageBeneficiariesScreen()));
|
||||||
}, disable: true),
|
}, disable: false),
|
||||||
_buildQuickLink(Symbols.support_agent,
|
_buildQuickLink(Symbols.support_agent,
|
||||||
AppLocalizations.of(context).contactUs, () {
|
AppLocalizations.of(context).contactUs, () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
|
@@ -70,7 +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(color: Colors.white70, fontSize: 12),
|
style: TextStyle(color: Theme.of(context).dialogBackgroundColor, fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@@ -106,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(
|
||||||
|
@@ -172,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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -201,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(),
|
||||||
|
@@ -5,7 +5,7 @@ import 'package:kmobile/features/auth/controllers/theme_cubit.dart';
|
|||||||
import 'package:kmobile/features/auth/controllers/theme_state.dart';
|
import 'package:kmobile/features/auth/controllers/theme_state.dart';
|
||||||
|
|
||||||
|
|
||||||
void showColorThemeDialog(BuildContext context) {
|
/*void showColorThemeDialog(BuildContext context) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
@@ -32,4 +32,50 @@ void showColorThemeDialog(BuildContext context) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
class ColorThemeDialog extends StatelessWidget {
|
||||||
|
const ColorThemeDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: const Text('Select Color Theme'),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const CircleAvatar(backgroundColor: Colors.deepPurple),
|
||||||
|
title: const Text('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: const Text('Blue'),
|
||||||
|
onTap: () {
|
||||||
|
context.read<ThemeCubit>().changeTheme(ThemeType.blue);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -22,21 +22,26 @@ class PreferenceScreen extends StatelessWidget {
|
|||||||
return ListView(
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
// Theme Mode Switch (Light/Dark)
|
// Theme Mode Switch (Light/Dark)
|
||||||
ListTile(
|
// ListTile(
|
||||||
leading: const Icon(Icons.brightness_6),
|
// leading: const Icon(Icons.brightness_6),
|
||||||
title: const Text("Theme Mode"),
|
// title: const Text("Theme Mode"),
|
||||||
trailing: Switch(
|
// trailing: Switch(
|
||||||
value: state.isDarkMode,
|
// value: state.isDarkMode,
|
||||||
onChanged: (val) {
|
// onChanged: (val) {
|
||||||
context.read<ThemeCubit>().toggleDarkMode(val);
|
// context.read<ThemeCubit>().toggleDarkMode(val);
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
//Color_Theme_Selection
|
//Color_Theme_Selection
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.color_lens),
|
leading: const Icon(Icons.color_lens),
|
||||||
title: const Text('Theme Color'),
|
title: const Text('Theme Color'),
|
||||||
onTap: () => showColorThemeDialog(context),
|
onTap: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => const ColorThemeDialog(),
|
||||||
|
);
|
||||||
|
}
|
||||||
),
|
),
|
||||||
// Language Selection
|
// Language Selection
|
||||||
ListTile(
|
ListTile(
|
||||||
|
Reference in New Issue
Block a user