This commit is contained in:
2025-12-01 12:58:17 +05:30
parent 8aa5b170ca
commit 8c7e94759a
9 changed files with 387 additions and 404 deletions

BIN
assets/images/profile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -12,10 +12,8 @@ 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';
import 'features/card/screens/card_management_screen.dart';
import 'features/accounts/screens/account_statement_screen.dart'; import 'features/accounts/screens/account_statement_screen.dart';
import 'package:kmobile/features/auth/controllers/auth_state.dart'; import 'package:kmobile/features/auth/controllers/auth_state.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';
@@ -330,7 +328,6 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
}, },
), ),
const CardManagementScreen(),
const ServiceScreen(), const ServiceScreen(),
]; ];
@@ -392,10 +389,6 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
icon: const Icon(Icons.swap_vert_sharp), icon: const Icon(Icons.swap_vert_sharp),
label: AppLocalizations.of(context).transactions, label: AppLocalizations.of(context).transactions,
), ),
BottomNavigationBarItem(
icon: const Icon(Icons.credit_card),
label: AppLocalizations.of(context).card,
),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: const Icon(Icons.miscellaneous_services), icon: const Icon(Icons.miscellaneous_services),
label: AppLocalizations.of(context).services, label: AppLocalizations.of(context).services,

View File

@@ -42,7 +42,7 @@ class _AccountInfoScreen extends State<AccountInfoScreen> {
return AppLocalizations.of(context).recurringDeposit; return AppLocalizations.of(context).recurringDeposit;
case 'ca': case 'ca':
return "Current Account"; return "Current Account";
case 'cc': case 'cc':
return "Cash Credit Account"; return "Cash Credit Account";
case 'od': case 'od':
return "Overdraft Account"; return "Overdraft Account";

View File

@@ -150,36 +150,42 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
AppLocalizations.of(context).accountNumber, AppLocalizations.of(context).accountNumber,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.w500, fontSize: 17), fontWeight: FontWeight.w500, fontSize: 17),
), ),
const VerticalDivider(width: 20, thickness: 1, indent: 5, endIndent: 5, color: Colors.grey), const VerticalDivider(
DropdownButton<User>( width: 20,
value: selectedUser, thickness: 1,
onChanged: (User? newUser) { indent: 5,
if (newUser != null) { endIndent: 5,
setState(() { color: Colors.grey),
selectedUser = newUser; DropdownButton<User>(
}); value: selectedUser,
_loadTransactions(); onChanged: (User? newUser) {
} if (newUser != null) {
}, setState(() {
items: widget.users.map((user) { selectedUser = newUser;
return DropdownMenuItem<User>( });
value: user, _loadTransactions();
child: Text(user.accountNo.toString()), }
); },
}).toList(), items: widget.users.map((user) {
underline: Container(), // Remove the underline return DropdownMenuItem<User>(
), value: user,
Spacer(), child: Text(user.accountNo.toString()),
], );
), ), }).toList(),
underline: Container(), // Remove the underline
),
Spacer(),
],
),
),
), ),
Card( Card(
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),

View File

@@ -26,149 +26,152 @@ class _CustomerInfoScreenState extends State<CustomerInfoScreen> {
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( title: Text(
AppLocalizations.of(context) AppLocalizations.of(context)
.customerInfo .customerInfo
.replaceFirst(RegExp('\n'), ''), .replaceFirst(RegExp('\n'), ''),
),
), ),
), body: SafeArea(
body: SafeArea( child: Stack(
child: Stack( children: [
children: [ SingleChildScrollView(
SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(),
physics: const AlwaysScrollableScrollPhysics(), child: Padding(
child: Padding( padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(16.0), child: Column(
child: Column( children: [
children: [ Card(
Card( elevation: 0,
elevation: 0, shape: RoundedRectangleBorder(
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(12), side: BorderSide(
side: BorderSide( color: theme.colorScheme.outline.withOpacity(0.2),
color: theme.colorScheme.outline.withOpacity(0.2), width: 1,
width: 1, ),
), ),
), child: Padding(
child: Padding( padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Row( child: Row(
children: [ children: [
SizedBox( SizedBox(
width: 56, width: 56,
height: 56, height: 56,
child: CircleAvatar( child: CircleAvatar(
radius: 50, radius: 50,
child: SvgPicture.asset( child: SvgPicture.asset(
'assets/images/avatar_male.svg', 'assets/images/avatar_male.svg',
fit: BoxFit.cover, fit: BoxFit.cover,
),
), ),
), ),
), const SizedBox(width: 12),
const SizedBox(width: 12), // Name + mobile
// Name + mobile Expanded(
Expanded( child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Text(
Text( // If you want to show the user's name instead, replace below.
// If you want to show the user's name instead, replace below. user.name ?? '',
user.name ?? '', style:
style: theme.textTheme.titleLarge?.copyWith(
theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w600,
fontWeight: FontWeight.w600, ),
), ),
), const SizedBox(height: 4),
const SizedBox(height: 4), Text(
Text( user.cifNumber ?? '',
user.cifNumber ?? '', style:
style: theme.textTheme.bodyMedium?.copyWith( theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurface color: theme.colorScheme.onSurface
.withOpacity(0.7), .withOpacity(0.7),
),
), ),
), ],
], ),
), ),
), ],
], ),
), ),
), ),
), const SizedBox(height: 16),
const SizedBox(height: 16), Card(
Card( elevation: 0,
elevation: 0, shape: RoundedRectangleBorder(
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(12), side: BorderSide(
side: BorderSide( color: theme.colorScheme.outline.withOpacity(0.2),
color: theme.colorScheme.outline.withOpacity(0.2), width: 1,
width: 1, ),
), ),
), child: Padding(
child: Padding( padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(16.0), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Text(
Text( 'Personal Information',
'Personal Information', style: theme.textTheme.titleMedium?.copyWith(
style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600,
fontWeight: FontWeight.w600, ),
), ),
), const SizedBox(height: 16),
const SizedBox(height: 16), InfoField(
InfoField( label:
label: AppLocalizations.of(context).activeAccounts, AppLocalizations.of(context).activeAccounts,
value: user.activeAccounts?.toString() ?? 'N/A', value: user.activeAccounts?.toString() ?? 'N/A',
), ),
InfoField( InfoField(
label: AppLocalizations.of(context).mobileNumber, label:
value: user.mobileNo ?? 'N/A', AppLocalizations.of(context).mobileNumber,
), value: user.mobileNo ?? 'N/A',
InfoField( ),
label: AppLocalizations.of(context).dateOfBirth, InfoField(
value: (user.dateOfBirth != null && label: AppLocalizations.of(context).dateOfBirth,
user.dateOfBirth!.length == 8) value: (user.dateOfBirth != null &&
? '${user.dateOfBirth!.substring(0, 2)}-${user.dateOfBirth!.substring(2, 4)}-${user.dateOfBirth!.substring(4, 8)}' user.dateOfBirth!.length == 8)
: 'N/A', ? '${user.dateOfBirth!.substring(0, 2)}-${user.dateOfBirth!.substring(2, 4)}-${user.dateOfBirth!.substring(4, 8)}'
), // Replace with DOB if available : 'N/A',
InfoField( ), // Replace with DOB if available
label: AppLocalizations.of(context).branchCode, InfoField(
value: user.branchId ?? 'N/A', label: AppLocalizations.of(context).branchCode,
), value: user.branchId ?? 'N/A',
InfoField( ),
label: AppLocalizations.of(context).address, InfoField(
value: user.address ?? 'N/A', label: AppLocalizations.of(context).address,
), // Replace with Aadhar if available value: user.address ?? 'N/A',
InfoField( ), // Replace with Aadhar if available
label: AppLocalizations.of(context).primaryId, InfoField(
value: _maskPrimaryId(user.primaryId), label: AppLocalizations.of(context).primaryId,
), value: _maskPrimaryId(user.primaryId),
], ),
],
),
), ),
), ),
), ],
], ),
), ),
), ),
), IgnorePointer(
IgnorePointer( child: Center(
child: Center( child: Opacity(
child: Opacity( opacity: 0.07, // Reduced opacity
opacity: 0.07, // Reduced opacity child: ClipOval(
child: ClipOval( child: Image.asset(
child: Image.asset( 'assets/images/logo.png',
'assets/images/logo.png', width: 200, // Adjust size as needed
width: 200, // Adjust size as needed height: 200, // Adjust size as needed
height: 200, // Adjust size as needed ),
), ),
), ),
), ),
), ),
), ],
], ),
), ));
)
);
} }
} }

View File

@@ -174,260 +174,240 @@ class _ProfileScreenState extends State<ProfileScreen> {
title: Text(loc.profile), title: Text(loc.profile),
elevation: 0, elevation: 0,
), ),
body: Stack( body: Stack(
children: [ children: [
ListView( ListView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
children: [ children: [
// ===== Profile Header ===== // ===== Profile Header =====
Card( Card(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Row( child: Row(
children: [
// Avatar
Container(
width: 56,
height: 56,
child: CircleAvatar(
radius: 50,
child: SvgPicture.asset(
'assets/images/avatar_male.svg',
fit: BoxFit.cover,
),
),
),
const SizedBox(width: 12),
// Name + mobile
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( // Avatar
// If you want to show the user's name instead, replace below. Container(
widget.customerName, width: 56,
style: theme.textTheme.titleLarge?.copyWith( height: 56,
fontWeight: FontWeight.w600, child: CircleAvatar(
radius: 50,
child: SvgPicture.asset(
'assets/images/avatar_male.svg',
fit: BoxFit.cover,
),
), ),
), ),
const SizedBox(height: 4), const SizedBox(width: 12),
Text( // Name + mobile
widget.customerNo, Expanded(
style: theme.textTheme.bodyMedium?.copyWith( child: Column(
color: theme.colorScheme.onSurface crossAxisAlignment: CrossAxisAlignment.start,
.withOpacity(0.7), children: [
Text(
// If you want to show the user's name instead, replace below.
widget.customerName,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
widget.customerNo,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurface
.withOpacity(0.7),
),
),
],
), ),
), ),
], ],
), ),
), ),
],
),
),
),
const SizedBox(height: 16),
// ===== Section: Settings =====
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"Settings",
style: theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
const SizedBox(height: 8),
_SectionTile(
leadingIcon: Icons.settings,
title: loc.preferences,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PreferenceScreen(),
), ),
);
}, const SizedBox(height: 16),
),
_SectionTile( // ===== Section: Settings =====
leadingIcon: Icons.security, Padding(
title: loc.securitySettings, padding: const EdgeInsets.symmetric(horizontal: 8.0),
onTap: () { child: Text(
Navigator.push( "Settings",
context, style: theme.textTheme.labelLarge?.copyWith(
MaterialPageRoute( fontWeight: FontWeight.w600,
builder: (context) => SecuritySettingsScreen( letterSpacing: 0.2,
mobileNumber: widget.mobileNumber, ),
), ),
), ),
); const SizedBox(height: 8),
},
),
_SectionTile(
leadingIcon: Icons.currency_rupee,
title: loc.dailylimit,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DailyLimitScreen(),
),
);
},
),
Card(
child: SwitchListTile(
title: Text(loc.enableFingerprintLogin),
value: _isBiometricEnabled,
onChanged: (bool value) {
_handleBiometricToggle(value);
},
secondary: const Icon(Icons.fingerprint),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
),
),
const SizedBox(height: 16), _SectionTile(
const Divider(height: 24), leadingIcon: Icons.settings,
title: loc.preferences,
// ===== Section: Security & App ===== onTap: () {
Padding( Navigator.push(
padding: const EdgeInsets.symmetric(horizontal: 8.0), context,
child: Text( MaterialPageRoute(
loc.appVersion, builder: (context) => const PreferenceScreen(),
style: theme.textTheme.labelLarge?.copyWith( ),
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
const SizedBox(height: 8),
// Fingerprint toggle inside a styled container
Card(
child: ListTile(
leading: const Icon(Icons.smartphone),
title: Text(loc.appVersion),
trailing: FutureBuilder<String>(
future: _getAppVersion(),
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
); );
} else if (snapshot.hasError) { },
return Text(loc.error); ),
} else { _SectionTile(
return Text( leadingIcon: Icons.security,
snapshot.data ?? "N/A", title: loc.securitySettings,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecuritySettingsScreen(
mobileNumber: widget.mobileNumber,
),
),
); );
} },
},
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
),
const SizedBox(height: 16),
const Divider(height: 24),
// ===== Section: Actions =====
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"Exit",
style: theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
const SizedBox(height: 8),
_SectionTile(
leadingIcon: Icons.exit_to_app,
title: loc.logout,
trailChevron: false, // action tile, no chevron
onTap: () async {
final shouldExit = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.logout),
content: Text(loc.logoutCheck),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.no),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.yes),
),
],
), ),
); _SectionTile(
leadingIcon: Icons.currency_rupee,
if (shouldExit == true) { title: loc.dailylimit,
if (Platform.isAndroid) { onTap: () {
SystemNavigator.pop(); Navigator.push(
} context,
exit(0); MaterialPageRoute(
} builder: (context) => const DailyLimitScreen(),
}, ),
), );
_SectionTile( },
leadingIcon: Icons.logout,
title: loc.deregister,
trailChevron: false,
onTap: () async {
final shouldLogout = await showDialog<bool>(
context: context,
builder: (_) => const LogoutDialog(),
);
if (shouldLogout == true) {
await _handleLogout(context);
}
},
),
const SizedBox(height: 24),
],
),
// ===== Watermark (kept subtle, no theme change) =====
IgnorePointer(
child: Positioned.fill(
child: Center(
child: Opacity(
opacity: 0.06,
child: ClipOval(
child: Image.asset(
'assets/images/logo.png',
width: 200,
height: 200,
filterQuality: FilterQuality.medium,
), ),
), Card(
child: SwitchListTile(
title: Text(loc.enableFingerprintLogin),
value: _isBiometricEnabled,
onChanged: (bool value) {
_handleBiometricToggle(value);
},
secondary: const Icon(Icons.fingerprint),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
),
),
const SizedBox(height: 16),
const Divider(height: 24),
// ===== Section: Security & App =====
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
loc.appVersion,
style: theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
const SizedBox(height: 8),
// Fingerprint toggle inside a styled container
Card(
child: ListTile(
leading: const Icon(Icons.smartphone),
title: Text(loc.appVersion),
trailing: FutureBuilder<String>(
future: _getAppVersion(),
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
);
} else if (snapshot.hasError) {
return Text(loc.error);
} else {
return Text(
snapshot.data ?? "N/A",
);
}
},
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
),
const SizedBox(height: 16),
const Divider(height: 24),
// ===== Section: Actions =====
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"Exit",
style: theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
const SizedBox(height: 8),
_SectionTile(
leadingIcon: Icons.exit_to_app,
title: loc.logout,
trailChevron: false, // action tile, no chevron
onTap: () async {
final shouldExit = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.logout),
content: Text(loc.logoutCheck),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.no),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.yes),
),
],
),
);
if (shouldExit == true) {
if (Platform.isAndroid) {
SystemNavigator.pop();
}
exit(0);
}
},
),
_SectionTile(
leadingIcon: Icons.logout,
title: loc.deregister,
trailChevron: false,
onTap: () async {
final shouldLogout = await showDialog<bool>(
context: context,
builder: (_) => const LogoutDialog(),
);
if (shouldLogout == true) {
await _handleLogout(context);
}
},
),
const SizedBox(height: 24),
],
), ),
), ],
), ), );
),
],
),
);
} }
} }

View File

@@ -15,7 +15,7 @@ void main() async {
]); ]);
// Check for device compromise // Check for device compromise
//final compromisedMessage = await SecurityService.deviceCompromisedMessage; // final compromisedMessage = await SecurityService.deviceCompromisedMessage;
// if (compromisedMessage != null) { // if (compromisedMessage != null) {
// runApp(MaterialApp( // runApp(MaterialApp(
// home: SecurityErrorScreen(message: compromisedMessage), // home: SecurityErrorScreen(message: compromisedMessage),

View File

@@ -115,6 +115,7 @@ flutter:
- assets/images/uco_logo.png - assets/images/uco_logo.png
- assets/images/ipos_logo.png - assets/images/ipos_logo.png
- assets/images/profile.svg - assets/images/profile.svg
- assets/images/profile.png
- assets/animations/rupee.json - assets/animations/rupee.json
- assets/animations/error.json - assets/animations/error.json
- assets/animations/done.json - assets/animations/done.json