- Implemented MPINAuthScreen for generating and entering MPIN. - Created MPINScreen for user interaction with MPIN input. - Developed OTPLoginScreen for OTP verification process. - Added request demo bottom sheet with organization form. - Enhanced DashboardScreen to check MPIN status and prompt user to generate MPIN if not set.
169 lines
3.9 KiB
Dart
169 lines
3.9 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
|
|
|
class OTPController extends GetxController {
|
|
final formKey = GlobalKey<FormState>();
|
|
|
|
// Observables
|
|
final RxString phoneNumber = ''.obs;
|
|
final RxBool isOTPSent = false.obs;
|
|
|
|
final RxBool isSending = false.obs;
|
|
final RxBool isResending = false.obs;
|
|
|
|
final RxInt timer = 0.obs;
|
|
Timer? _countdownTimer;
|
|
|
|
// Text controllers and focus nodes
|
|
final TextEditingController phoneController = TextEditingController();
|
|
final List<TextEditingController> otpControllers = List.generate(4, (_) => TextEditingController());
|
|
final List<FocusNode> focusNodes = List.generate(4, (_) => FocusNode());
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
timer.value = 0;
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_countdownTimer?.cancel();
|
|
phoneController.dispose();
|
|
for (final controller in otpControllers) {
|
|
controller.dispose();
|
|
}
|
|
for (final node in focusNodes) {
|
|
node.dispose();
|
|
}
|
|
super.onClose();
|
|
}
|
|
|
|
static Future<bool> _sendOTP(String phone) async {
|
|
await Future.delayed(const Duration(seconds: 2));
|
|
debugPrint('Sending OTP to $phone');
|
|
return true;
|
|
}
|
|
|
|
Future<void> sendOTP() async {
|
|
final phone = phoneController.text.trim();
|
|
|
|
if (!_validatePhone(phone)) {
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: "Please enter a valid 10-digit mobile number",
|
|
type: SnackbarType.error,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (isSending.value) return;
|
|
isSending.value = true;
|
|
|
|
final success = await _sendOTP(phone);
|
|
if (success) {
|
|
phoneNumber.value = phone;
|
|
isOTPSent.value = true;
|
|
_startTimer();
|
|
_clearOTPFields();
|
|
} else {
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: "Failed to send OTP. Please try again.",
|
|
type: SnackbarType.error,
|
|
);
|
|
}
|
|
|
|
isSending.value = false;
|
|
}
|
|
|
|
Future<void> onResendOTP() async {
|
|
if (isResending.value) return;
|
|
isResending.value = true;
|
|
|
|
_clearOTPFields();
|
|
|
|
final success = await _sendOTP(phoneNumber.value);
|
|
if (success) {
|
|
_startTimer();
|
|
} else {
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: "Failed to resend OTP. Please try again.",
|
|
type: SnackbarType.error,
|
|
);
|
|
}
|
|
|
|
isResending.value = false;
|
|
}
|
|
|
|
void onOTPChanged(String value, int index) {
|
|
if (value.isNotEmpty) {
|
|
if (index < otpControllers.length - 1) {
|
|
focusNodes[index + 1].requestFocus();
|
|
} else {
|
|
focusNodes[index].unfocus();
|
|
}
|
|
} else {
|
|
if (index > 0) {
|
|
focusNodes[index - 1].requestFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void verifyOTP() {
|
|
final enteredOTP = otpControllers.map((c) => c.text).join();
|
|
if (enteredOTP.length != otpControllers.length || enteredOTP.contains('')) {
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: "Please enter the complete ${otpControllers.length}-digit OTP",
|
|
type: SnackbarType.error,
|
|
);
|
|
return;
|
|
}
|
|
|
|
// TODO: Add your OTP verification logic
|
|
debugPrint('Verifying OTP: $enteredOTP');
|
|
}
|
|
|
|
void _clearOTPFields() {
|
|
for (final controller in otpControllers) {
|
|
controller.clear();
|
|
}
|
|
focusNodes[0].requestFocus();
|
|
}
|
|
|
|
void _startTimer() {
|
|
timer.value = 60;
|
|
_countdownTimer?.cancel();
|
|
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (this.timer.value > 0) {
|
|
this.timer.value--;
|
|
} else {
|
|
timer.cancel();
|
|
}
|
|
});
|
|
}
|
|
|
|
void resetForChangeNumber() {
|
|
isOTPSent.value = false;
|
|
phoneNumber.value = '';
|
|
phoneController.clear();
|
|
_clearOTPFields();
|
|
|
|
timer.value = 0;
|
|
isSending.value = false;
|
|
isResending.value = false;
|
|
|
|
for (final node in focusNodes) {
|
|
node.unfocus();
|
|
}
|
|
}
|
|
|
|
bool _validatePhone(String phone) {
|
|
final regex = RegExp(r'^\d{10}$');
|
|
return regex.hasMatch(phone);
|
|
}
|
|
}
|