|
|
|
|
@ -1,15 +1,21 @@
|
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
|
|
|
import 'package:flutter/animation.dart';
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
import 'package:get_it/get_it.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/core/cache_consts.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/core/enums.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/services/cache_service.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
|
|
|
|
|
@ -53,7 +59,8 @@ class OTPWidget extends StatefulWidget {
|
|
|
|
|
final FocusNode? focusNode;
|
|
|
|
|
final AnimatedSwitcherTransitionBuilder? pinTextAnimatedSwitcherTransition;
|
|
|
|
|
final Duration pinTextAnimatedSwitcherDuration;
|
|
|
|
|
final TextDirection textDirection;
|
|
|
|
|
|
|
|
|
|
// final TextDirection textDirection;
|
|
|
|
|
final TextInputType keyboardType;
|
|
|
|
|
final EdgeInsets pinBoxOuterPadding;
|
|
|
|
|
|
|
|
|
|
@ -74,7 +81,6 @@ class OTPWidget extends StatefulWidget {
|
|
|
|
|
this.onTextChanged,
|
|
|
|
|
this.autoFocus = false,
|
|
|
|
|
this.focusNode,
|
|
|
|
|
this.textDirection = TextDirection.ltr,
|
|
|
|
|
this.keyboardType = TextInputType.number,
|
|
|
|
|
this.pinBoxOuterPadding = const EdgeInsets.symmetric(horizontal: 4.0),
|
|
|
|
|
this.pinBoxColor = Colors.white,
|
|
|
|
|
@ -182,17 +188,9 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
|
|
|
|
|
widget.controller!.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove focus from the input
|
|
|
|
|
if (focusNode.hasFocus) {
|
|
|
|
|
focusNode.unfocus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Optionally refocus after a short delay to allow user to re-enter OTP
|
|
|
|
|
Future.delayed(const Duration(milliseconds: 100), () {
|
|
|
|
|
if (mounted && widget.autoFocus) {
|
|
|
|
|
FocusScope.of(context).requestFocus(focusNode);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -402,58 +400,6 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Widget _buildPinCode(int i, BuildContext context) {
|
|
|
|
|
// Color pinBoxColor = widget.pinBoxColor;
|
|
|
|
|
//
|
|
|
|
|
// if (widget.hasError) {
|
|
|
|
|
// pinBoxColor = widget.errorBorderColor;
|
|
|
|
|
// } else if (i < text.length) {
|
|
|
|
|
// pinBoxColor = AppColors.blackBgColor; // Custom color for filled boxes
|
|
|
|
|
// } else {
|
|
|
|
|
// pinBoxColor = widget.pinBoxColor;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// // Change color to success when all fields are complete
|
|
|
|
|
// if (text.length == widget.maxLength) {
|
|
|
|
|
// pinBoxColor = AppColors.successColor;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// EdgeInsets insets;
|
|
|
|
|
// if (i == 0) {
|
|
|
|
|
// insets = EdgeInsets.only(
|
|
|
|
|
// left: 0,
|
|
|
|
|
// top: widget.pinBoxOuterPadding.top,
|
|
|
|
|
// right: widget.pinBoxOuterPadding.right,
|
|
|
|
|
// bottom: widget.pinBoxOuterPadding.bottom,
|
|
|
|
|
// );
|
|
|
|
|
// } else if (i == strList.length - 1) {
|
|
|
|
|
// insets = EdgeInsets.only(
|
|
|
|
|
// left: widget.pinBoxOuterPadding.left,
|
|
|
|
|
// top: widget.pinBoxOuterPadding.top,
|
|
|
|
|
// right: 0,
|
|
|
|
|
// bottom: widget.pinBoxOuterPadding.bottom,
|
|
|
|
|
// );
|
|
|
|
|
// } else {
|
|
|
|
|
// insets = widget.pinBoxOuterPadding;
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// return AnimatedContainer(
|
|
|
|
|
// duration: const Duration(milliseconds: 200),
|
|
|
|
|
// curve: Curves.easeInOut,
|
|
|
|
|
// key: ValueKey<String>("container$i"),
|
|
|
|
|
// alignment: Alignment.center,
|
|
|
|
|
// padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 1.0),
|
|
|
|
|
// margin: insets,
|
|
|
|
|
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
|
|
|
|
// color: pinBoxColor,
|
|
|
|
|
// borderRadius: widget.pinBoxRadius,
|
|
|
|
|
// ),
|
|
|
|
|
// width: widget.pinBoxWidth,
|
|
|
|
|
// height: widget.pinBoxHeight,
|
|
|
|
|
// child: _animatedTextBox(strList[i], i),
|
|
|
|
|
// );
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
Widget _animatedTextBox(String text, int i) {
|
|
|
|
|
if (widget.pinTextAnimatedSwitcherTransition != null) {
|
|
|
|
|
return AnimatedSwitcher(
|
|
|
|
|
@ -586,6 +532,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
AuthenticationViewModel authVM = context.read<AuthenticationViewModel>();
|
|
|
|
|
return Scaffold(
|
|
|
|
|
backgroundColor: AppColors.scaffoldBgColor,
|
|
|
|
|
appBar: CustomAppBar(
|
|
|
|
|
@ -601,19 +548,22 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
SizedBox(height: 40.h),
|
|
|
|
|
Text(
|
|
|
|
|
'OTP Verification',
|
|
|
|
|
style: TextStyle(fontSize: 24.fSize, fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
SizedBox(height: 16.h),
|
|
|
|
|
Text(
|
|
|
|
|
'We have sent you the OTP code on ${_getMaskedPhoneNumber()} via SMS for registration verification',
|
|
|
|
|
style: TextStyle(fontSize: 16.fSize, color: Colors.grey),
|
|
|
|
|
SizedBox(height: 10.h),
|
|
|
|
|
LocaleKeys.otpVerification.tr().toText24(isBold: true),
|
|
|
|
|
SizedBox(height: 20.h),
|
|
|
|
|
Wrap(
|
|
|
|
|
spacing: 4.h,
|
|
|
|
|
runSpacing: 8.0,
|
|
|
|
|
children: [
|
|
|
|
|
LocaleKeys.weHaveSendOTP.tr().toText16(color: AppColors.inputLabelTextColor),
|
|
|
|
|
_getMaskedPhoneNumber().toText16(color: AppColors.inputLabelTextColor, isBold: true),
|
|
|
|
|
LocaleKeys.via.tr().toText16(color: AppColors.inputLabelTextColor),
|
|
|
|
|
authVM.loginTypeEnum.displayName.toText16(color: AppColors.inputLabelTextColor),
|
|
|
|
|
LocaleKeys.forRegistrationVerification.tr().toText16(color: AppColors.inputLabelTextColor),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
SizedBox(height: 40.h),
|
|
|
|
|
|
|
|
|
|
// OTP Input Fields using new OTP Widget
|
|
|
|
|
SizedBox(height: 16.h),
|
|
|
|
|
Center(
|
|
|
|
|
child: OTPWidget(
|
|
|
|
|
maxLength: _otpLength,
|
|
|
|
|
@ -635,38 +585,32 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
|
SizedBox(height: 32.h),
|
|
|
|
|
|
|
|
|
|
// Resend OTP
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
children: [
|
|
|
|
|
const Text("Didn't receive it? "),
|
|
|
|
|
LocaleKeys.didntReceiveIt.tr().toText16(color: AppColors.inputLabelTextColor),
|
|
|
|
|
SizedBox(width: 5.h),
|
|
|
|
|
if (_resendTime > 0)
|
|
|
|
|
Builder(
|
|
|
|
|
// Use a Builder to easily calculate minutes and seconds inline
|
|
|
|
|
builder: (context) {
|
|
|
|
|
final minutes = (_resendTime ~/ 60)
|
|
|
|
|
.toString()
|
|
|
|
|
.padLeft(2, '0'); // Integer division for minutes final seconds = (_resendTime % 60).toString().padLeft(2, '0'); // Modulo for remaining seconds
|
|
|
|
|
final seconds = (_resendTime % 60).toString().padLeft(2, '0'); // Modulo for remaining seconds // <--- HERE IT IS
|
|
|
|
|
return Text(
|
|
|
|
|
'resend in ($minutes:$seconds). ',
|
|
|
|
|
style: const TextStyle(color: Colors.grey),
|
|
|
|
|
final minutes = (_resendTime ~/ 60).toString().padLeft(2, '0');
|
|
|
|
|
final seconds = (_resendTime % 60).toString().padLeft(2, '0');
|
|
|
|
|
return Row(
|
|
|
|
|
children: [
|
|
|
|
|
LocaleKeys.resendIn.tr().toText16(color: AppColors.inputLabelTextColor),
|
|
|
|
|
SizedBox(width: 2.h),
|
|
|
|
|
' ($minutes:$seconds). '.toText16(color: AppColors.inputLabelTextColor)
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: _resendOtp,
|
|
|
|
|
child: const Text(
|
|
|
|
|
'Resend',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
color: AppColors.primaryRedColor,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
child: LocaleKeys.resendOTP.tr().toText16(color: AppColors.primaryRedColor),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
@ -678,7 +622,6 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _verifyOtp(String otp) {
|
|
|
|
|
debugPrint('Verifying OTP: $otp');
|
|
|
|
|
widget.checkActivationCode(int.parse(otp));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -689,6 +632,6 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
|
|
|
|
|
_isVerifying = false;
|
|
|
|
|
_otpController.text = otp;
|
|
|
|
|
setState(() {});
|
|
|
|
|
_onOtpChanged(otp); // Ensure verification and color update
|
|
|
|
|
_onOtpChanged(otp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|