Change TPIn #2

This commit is contained in:
2025-11-08 12:25:56 +05:30
parent 3417c4b0e5
commit 87fd36b748
4 changed files with 265 additions and 5 deletions

View File

@@ -5,6 +5,7 @@ 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/logout_dialog.dart';
import 'package:kmobile/features/profile/tpin/change_tpin_screen.dart';
import 'package:kmobile/security/secure_storage.dart';
import 'package:local_auth/local_auth.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -12,6 +13,9 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../di/injection.dart';
import '../../l10n/app_localizations.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 {
final String mobileNumber;
@@ -190,11 +194,55 @@ class _ProfileScreenState extends State<ProfileScreen> {
},
),
ListTile(
leading: const Icon(Icons.password),
title: const Text("Change TPIN"),
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) => const ChangeTpinScreen(),
),
);
}
},
),
// ListTile(
// leading: const Icon(Icons.password),
// title: const Text("Change Login MPIN"),

View File

@@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:kmobile/widgets/pin_input_field.dart'; // Use the new widget
class ChangeTpinOtpScreen extends StatefulWidget {
final String newTpin;
const ChangeTpinOtpScreen({super.key, required this.newTpin});
@override
State<ChangeTpinOtpScreen> createState() => _ChangeTpinOtpScreenState();
}
class _ChangeTpinOtpScreenState extends State<ChangeTpinOtpScreen> {
final _otpController = TextEditingController();
void _handleVerifyOtp() {
// TODO: Add API call to verify OTP and change TPIN
print('Verifying OTP: ${_otpController.text} for new TPIN: ${widget.newTpin}');
Navigator.of(context).popUntil((route) => route.isFirst);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Verify OTP'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 24),
const Text(
'Enter the OTP sent to your registered mobile number.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 32),
PinInputField(
controller: _otpController,
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: _handleVerifyOtp,
child: const Text('Verify & Change TPIN'),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:kmobile/features/profile/tpin/change_tpin_otp_screen.dart';
import 'package:kmobile/widgets/pin_input_field.dart'; // Use the new widget
class ChangeTpinScreen extends StatefulWidget {
const ChangeTpinScreen({super.key});
@override
State<ChangeTpinScreen> createState() => _ChangeTpinScreenState();
}
class _ChangeTpinScreenState extends State<ChangeTpinScreen> {
final _formKey = GlobalKey<FormState>();
final _oldTpinController = TextEditingController();
final _newTpinController = TextEditingController();
final _confirmTpinController = TextEditingController();
@override
void dispose() {
_oldTpinController.dispose();
_newTpinController.dispose();
_confirmTpinController.dispose();
super.dispose();
}
void _handleChangeTpin() {
if (_formKey.currentState!.validate()) {
// TODO: Add API call to request OTP for TPIN change
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChangeTpinOtpScreen(
newTpin: _newTpinController.text,
),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Change TPIN'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Current TPIN'),
const SizedBox(height: 8),
PinInputField(
controller: _oldTpinController,
validator: (value) {
if (value == null || value.length != 6) {
return 'Please enter your 6-digit old TPIN';
}
return null;
},
),
const SizedBox(height: 24),
const Text('New TPIN'),
const SizedBox(height: 8),
PinInputField(
controller: _newTpinController,
validator: (value) {
if (value == null || value.length != 6) {
return 'Please enter a 6-digit new TPIN';
}
return null;
},
),
const SizedBox(height: 24),
const Text('Confirm New TPIN'),
const SizedBox(height: 8),
PinInputField(
controller: _confirmTpinController,
validator: (value) {
if (value == null || value.length != 6) {
return 'Please confirm your new TPIN';
}
if (value != _newTpinController.text) {
return 'TPINs do not match';
}
return null;
},
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: _handleChangeTpin,
child: const Text('Proceed'),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class PinInputField extends StatelessWidget {
final TextEditingController controller;
final int length;
final FormFieldValidator<String>? validator;
const PinInputField({
super.key,
required this.controller,
this.length = 6,
this.validator,
});
@override
Widget build(BuildContext context) {
return FormField<String>(
validator: validator,
builder: (FormFieldState<String> state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: controller,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(length),
],
obscureText: true,
obscuringCharacter: '*',
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: 'Enter $length-digit PIN',
counterText: '', // Hide the counter
),
onChanged: (value) {
state.didChange(value);
},
),
if (state.hasError)
Padding(
padding: const EdgeInsets.only(top: 8.0, left: 12.0),
child: Text(
state.errorText!,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 12,
),
),
),
],
);
},
);
}
}