Security settings improvements
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -44,3 +44,8 @@ app.*.map.json
|
|||||||
lib/l10n/app_localizations.dart
|
lib/l10n/app_localizations.dart
|
||||||
lib/l10n/app_localizations_en.dart
|
lib/l10n/app_localizations_en.dart
|
||||||
lib/l10n/app_localizations_hi.dart
|
lib/l10n/app_localizations_hi.dart
|
||||||
|
|
||||||
|
# Keystore files
|
||||||
|
android/key.properties
|
||||||
|
android/*.jks
|
||||||
|
android/*.keystore
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ if (flutterVersionName == null) {
|
|||||||
flutterVersionName = '1.0'
|
flutterVersionName = '1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.example.kmobile"
|
namespace "com.example.kmobile"
|
||||||
compileSdk flutter.compileSdkVersion
|
compileSdk flutter.compileSdkVersion
|
||||||
@@ -51,15 +57,21 @@ android {
|
|||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
signingConfig signingConfigs.release
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
|
||||||
signingConfig signingConfigs.debug
|
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ 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', //test
|
//'http://lb-test-mobile-banking-app-192209417.ap-south-1.elb.amazonaws.com:8080', //test
|
||||||
//'http://lb-kccb-mobile-banking-app-848675342.ap-south-1.elb.amazonaws.com', //prod
|
//'http://lb-kccb-mobile-banking-app-848675342.ap-south-1.elb.amazonaws.com', //prod
|
||||||
//'https://kccbmbnk.net', //prod small
|
'https://kccbmbnk.net', //prod small
|
||||||
connectTimeout: const Duration(seconds: 60),
|
connectTimeout: const Duration(seconds: 60),
|
||||||
receiveTimeout: const Duration(seconds: 60),
|
receiveTimeout: const Duration(seconds: 60),
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -15,12 +15,18 @@ class MPinScreen extends StatefulWidget {
|
|||||||
final MPinMode mode;
|
final MPinMode mode;
|
||||||
final String? initialPin;
|
final String? initialPin;
|
||||||
final void Function(String pin)? onCompleted;
|
final void Function(String pin)? onCompleted;
|
||||||
|
final bool disableBiometric;
|
||||||
|
final String? customTitle;
|
||||||
|
final String? customConfirmTitle;
|
||||||
|
|
||||||
const MPinScreen({
|
const MPinScreen({
|
||||||
super.key,
|
super.key,
|
||||||
required this.mode,
|
required this.mode,
|
||||||
this.initialPin,
|
this.initialPin,
|
||||||
this.onCompleted,
|
this.onCompleted,
|
||||||
|
this.disableBiometric = false,
|
||||||
|
this.customTitle,
|
||||||
|
this.customConfirmTitle,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -77,7 +83,7 @@ class _MPinScreenState extends State<MPinScreen> with TickerProviderStateMixin {
|
|||||||
CurvedAnimation(parent: _waveController, curve: Curves.easeInOut),
|
CurvedAnimation(parent: _waveController, curve: Curves.easeInOut),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.mode == MPinMode.enter) {
|
if (widget.mode == MPinMode.enter && !widget.disableBiometric) {
|
||||||
_tryBiometricBeforePin();
|
_tryBiometricBeforePin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,17 +178,27 @@ class _MPinScreenState extends State<MPinScreen> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MPinMode.set:
|
case MPinMode.set:
|
||||||
// propagate parent onCompleted into confirm step
|
// Navigate to confirm and wait for result
|
||||||
Navigator.push(
|
final result = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => MPinScreen(
|
builder: (_) => MPinScreen(
|
||||||
mode: MPinMode.confirm,
|
mode: MPinMode.confirm,
|
||||||
initialPin: pin,
|
initialPin: pin,
|
||||||
onCompleted: widget.onCompleted, // <-- use parent callback
|
onCompleted: (confirmedPin) {
|
||||||
|
// Just pop with the pin, don't call parent callback yet
|
||||||
|
Navigator.of(context).pop(confirmedPin);
|
||||||
|
},
|
||||||
|
disableBiometric: widget.disableBiometric,
|
||||||
|
customTitle: widget.customConfirmTitle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If confirm succeeded, call parent callback
|
||||||
|
if (result != null && mounted) {
|
||||||
|
widget.onCompleted?.call(result);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MPinMode.confirm:
|
case MPinMode.confirm:
|
||||||
if (widget.initialPin == pin) {
|
if (widget.initialPin == pin) {
|
||||||
@@ -335,6 +351,9 @@ class _MPinScreenState extends State<MPinScreen> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getTitle() {
|
String getTitle() {
|
||||||
|
if (widget.customTitle != null) {
|
||||||
|
return widget.customTitle!;
|
||||||
|
}
|
||||||
switch (widget.mode) {
|
switch (widget.mode) {
|
||||||
case MPinMode.enter:
|
case MPinMode.enter:
|
||||||
return AppLocalizations.of(context).enterMPIN;
|
return AppLocalizations.of(context).enterMPIN;
|
||||||
|
|||||||
@@ -51,30 +51,16 @@ class _TpinOtpScreenState extends State<TpinOtpScreen> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TESTING BYPASS: Accept any 6-digit number as valid OTP
|
await _changePasswordService.validateOtp(
|
||||||
if (_enteredOtp.length == 6) {
|
otp: _enteredOtp,
|
||||||
// Skip API validation for testing
|
mobileNumber: widget.mobileNumber,
|
||||||
await Future.delayed(const Duration(milliseconds: 500)); // Simulate API delay
|
);
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (_) => const TpinSetScreen()),
|
MaterialPageRoute(builder: (_) => const TpinSetScreen()),
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Regular validation
|
|
||||||
await _changePasswordService.validateOtp(
|
|
||||||
otp: _enteredOtp,
|
|
||||||
mobileNumber: widget.mobileNumber,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
Navigator.pushReplacement(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (_) => const TpinSetScreen()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
96
lib/features/profile/change_mpin_screen.dart
Normal file
96
lib/features/profile/change_mpin_screen.dart
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:kmobile/features/auth/screens/mpin_screen.dart';
|
||||||
|
import 'package:kmobile/security/secure_storage.dart';
|
||||||
|
import 'package:kmobile/di/injection.dart';
|
||||||
|
import '../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class ChangeMpinScreen extends StatefulWidget {
|
||||||
|
const ChangeMpinScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChangeMpinScreen> createState() => _ChangeMpinScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChangeMpinScreenState extends State<ChangeMpinScreen> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// Start the flow after the widget is built
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_startChangeMpin();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startChangeMpin() async {
|
||||||
|
final loc = AppLocalizations.of(context);
|
||||||
|
|
||||||
|
// Step 1: Verify old PIN
|
||||||
|
final oldPinVerified = await Navigator.of(context).push<bool>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => MPinScreen(
|
||||||
|
mode: MPinMode.enter,
|
||||||
|
disableBiometric: true,
|
||||||
|
customTitle: loc.enterOldMpin,
|
||||||
|
onCompleted: (oldPin) => _verifyOldPin(oldPin),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (oldPinVerified != true) {
|
||||||
|
if (mounted) Navigator.of(context).pop(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2 & 3: Set new PIN (which will internally navigate to confirm)
|
||||||
|
// The onCompleted will be called after both set and confirm succeed
|
||||||
|
final success = await Navigator.of(context).push<bool>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => MPinScreen(
|
||||||
|
mode: MPinMode.set,
|
||||||
|
customTitle: loc.enterNewMpin,
|
||||||
|
customConfirmTitle: loc.confirmNewMpin,
|
||||||
|
onCompleted: (newPin) async {
|
||||||
|
// This is called after confirm succeeds and PIN is saved
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop(success == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _verifyOldPin(String oldPin) async {
|
||||||
|
final storage = getIt<SecureStorage>();
|
||||||
|
final storedPin = await storage.read('mpin');
|
||||||
|
|
||||||
|
if (storedPin == int.tryParse(oldPin)) {
|
||||||
|
// Old PIN is correct
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This shouldn't happen as MPinScreen handles validation
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pop(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(AppLocalizations.of(context).changeMpin),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,9 @@ import 'dart:io';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:kmobile/data/repositories/auth_repository.dart';
|
import 'package:kmobile/data/repositories/auth_repository.dart';
|
||||||
import 'package:kmobile/features/profile/change_password/change_password_screen.dart';
|
|
||||||
import 'package:kmobile/features/profile/daily_transaction_limit.dart';
|
import 'package:kmobile/features/profile/daily_transaction_limit.dart';
|
||||||
import 'package:kmobile/features/profile/logout_dialog.dart';
|
import 'package:kmobile/features/profile/logout_dialog.dart';
|
||||||
import 'package:kmobile/features/profile/tpin/change_tpin_screen.dart';
|
import 'package:kmobile/features/profile/security_settings_screen.dart';
|
||||||
import 'package:kmobile/security/secure_storage.dart';
|
import 'package:kmobile/security/secure_storage.dart';
|
||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
@@ -13,8 +12,6 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import '../../di/injection.dart';
|
import '../../di/injection.dart';
|
||||||
import '../../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';
|
||||||
import 'package:kmobile/api/services/auth_service.dart';
|
|
||||||
import 'package:kmobile/features/fund_transfer/screens/tpin_set_screen.dart';
|
|
||||||
|
|
||||||
class ProfileScreen extends StatefulWidget {
|
class ProfileScreen extends StatefulWidget {
|
||||||
final String mobileNumber;
|
final String mobileNumber;
|
||||||
@@ -201,77 +198,20 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
secondary: const Icon(Icons.fingerprint),
|
secondary: const Icon(Icons.fingerprint),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.password),
|
leading: const Icon(Icons.security),
|
||||||
title: Text(loc.changeLoginPassword),
|
title: Text(loc.securitySettings),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => ChangePasswordScreen(
|
builder: (context) => SecuritySettingsScreen(
|
||||||
mobileNumber: widget.mobileNumber,
|
mobileNumber: widget.mobileNumber,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.password),
|
|
||||||
title: Text('Change TPIN'),
|
|
||||||
onTap: () async {
|
|
||||||
// 1. Get the AuthService instance
|
|
||||||
final authService = getIt<AuthService>();
|
|
||||||
|
|
||||||
// 2. Call checkTpin() to see if TPIN is set
|
|
||||||
final isTpinSet = await authService.checkTpin();
|
|
||||||
|
|
||||||
// 3. If TPIN is not set, show the dialog
|
|
||||||
if (!isTpinSet) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text('TPIN Not Set'),
|
|
||||||
content: Text(
|
|
||||||
'You have not set a TPIN yet. Please set a TPIN to proceed.'),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: Text('Back'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: Text('Proceed'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop(); // Dismiss the dialog
|
|
||||||
// Navigate to the TPIN set screen
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => TpinSetScreen(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Case 2: TPIN is set
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
ChangeTpinScreen(mobileNumber: widget.mobileNumber),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// ListTile(
|
|
||||||
// leading: const Icon(Icons.password),
|
|
||||||
// title: const Text("Change Login MPIN"),
|
|
||||||
// onTap: () async {
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.smartphone),
|
leading: const Icon(Icons.smartphone),
|
||||||
title: const Text("App Version"),
|
title: const Text("App Version"),
|
||||||
|
|||||||
121
lib/features/profile/security_settings_screen.dart
Normal file
121
lib/features/profile/security_settings_screen.dart
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:kmobile/features/profile/change_password/change_password_screen.dart';
|
||||||
|
import 'package:kmobile/features/profile/tpin/change_tpin_screen.dart';
|
||||||
|
import 'package:kmobile/features/profile/change_mpin_screen.dart';
|
||||||
|
import 'package:kmobile/api/services/auth_service.dart';
|
||||||
|
import 'package:kmobile/features/fund_transfer/screens/tpin_set_screen.dart';
|
||||||
|
import 'package:kmobile/di/injection.dart';
|
||||||
|
import '../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class SecuritySettingsScreen extends StatelessWidget {
|
||||||
|
final String mobileNumber;
|
||||||
|
|
||||||
|
const SecuritySettingsScreen({super.key, required this.mobileNumber});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final loc = AppLocalizations.of(context);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(loc.securitySettings),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.lock_outline),
|
||||||
|
title: Text(loc.changeLoginPassword),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ChangePasswordScreen(
|
||||||
|
mobileNumber: mobileNumber,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.pin),
|
||||||
|
title: Text(loc.changeMpin),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () async {
|
||||||
|
final result = await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const ChangeMpinScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == true && context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(loc.mpinChangedSuccessfully),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.password),
|
||||||
|
title: const Text('Change TPIN'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () async {
|
||||||
|
final authService = getIt<AuthService>();
|
||||||
|
final isTpinSet = await authService.checkTpin();
|
||||||
|
|
||||||
|
if (!isTpinSet) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('TPIN Not Set'),
|
||||||
|
content: const Text(
|
||||||
|
'You have not set a TPIN yet. Please set a TPIN to proceed.'),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Back'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Proceed'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const TpinSetScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
ChangeTpinScreen(mobileNumber: mobileNumber),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -217,6 +217,13 @@
|
|||||||
"enterMPIN": "Enter your mPIN",
|
"enterMPIN": "Enter your mPIN",
|
||||||
"setMPIN": "Set your mPIN",
|
"setMPIN": "Set your mPIN",
|
||||||
"confirmMPIN": "Confirm your mPIN",
|
"confirmMPIN": "Confirm your mPIN",
|
||||||
|
"changeMpin": "Change mPIN",
|
||||||
|
"enterOldMpin": "Enter your old mPIN",
|
||||||
|
"enterNewMpin": "Enter your new mPIN",
|
||||||
|
"confirmNewMpin": "Confirm your new mPIN",
|
||||||
|
"mpinChangedSuccessfully": "mPIN changed successfully",
|
||||||
|
"pinMismatch": "PINs do not match",
|
||||||
|
"securitySettings": "Security Settings",
|
||||||
"kconnect": "Kconnect",
|
"kconnect": "Kconnect",
|
||||||
"kccBankFull": "Kangra Central Co-operative Bank",
|
"kccBankFull": "Kangra Central Co-operative Bank",
|
||||||
"themeColor": "Theme Color",
|
"themeColor": "Theme Color",
|
||||||
|
|||||||
@@ -218,6 +218,13 @@
|
|||||||
"enterMPIN": "अपना mPIN दर्ज करें",
|
"enterMPIN": "अपना mPIN दर्ज करें",
|
||||||
"setMPIN": "अपना mPIN सेट करें",
|
"setMPIN": "अपना mPIN सेट करें",
|
||||||
"confirmMPIN": "अपना mPIN की पुष्टि करें",
|
"confirmMPIN": "अपना mPIN की पुष्टि करें",
|
||||||
|
"changeMpin": "mPIN बदलें",
|
||||||
|
"enterOldMpin": "अपना पुराना mPIN दर्ज करें",
|
||||||
|
"enterNewMpin": "अपना नया mPIN दर्ज करें",
|
||||||
|
"confirmNewMpin": "अपना नया mPIN की पुष्टि करें",
|
||||||
|
"mpinChangedSuccessfully": "mPIN सफलतापूर्वक बदल गया",
|
||||||
|
"pinMismatch": "PIN मेल नहीं खा रहे हैं",
|
||||||
|
"securitySettings": "सुरक्षा सेटिंग्स",
|
||||||
"kconnect": "के-कनेक्ट",
|
"kconnect": "के-कनेक्ट",
|
||||||
"kccBankFull": "कांगड़ा सेंट्रल को-ऑपरेटिव बैंक",
|
"kccBankFull": "कांगड़ा सेंट्रल को-ऑपरेटिव बैंक",
|
||||||
"themeColor": "थीम रंग",
|
"themeColor": "थीम रंग",
|
||||||
|
|||||||
Reference in New Issue
Block a user