marco.pms.mobileapp/lib/controller/auth/otp_controller.dart
Vaibhav Surve 25dfcf3e08 feat: Add MPIN authentication and OTP login screens
- 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.
2025-06-10 10:04:18 +05:30

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);
}
}