import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/widgets/my_button.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text_style.dart'; import 'package:marco/controller/auth/otp_controller.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; class OTPLoginScreen extends StatefulWidget { const OTPLoginScreen({super.key}); @override State createState() => _OTPLoginScreenState(); } class _OTPLoginScreenState extends State with UIMixin { late final OTPController controller; final GlobalKey _otpFormKey = GlobalKey(); @override void initState() { controller = Get.put(OTPController()); super.initState(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Obx(() { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodyMedium('OTP Verification', fontWeight: 600), MySpacing.height(24), if (!controller.isOTPSent.value) ...[ _buildPhoneInput(), MySpacing.height(24), _buildSendOTPButton(theme), ] else ...[ _buildOTPSentInfo(), MySpacing.height(12), _buildOTPForm(theme), MySpacing.height(12), _buildResendOTPSection(theme), MySpacing.height(12), _buildVerifyOTPButton(theme), ], ], ), ); }); } Widget _buildPhoneInput() { return TextFormField( controller: controller.phoneController, keyboardType: TextInputType.phone, maxLength: 10, inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: InputDecoration( labelText: 'Enter Mobile Number', border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), counterText: '', ), onFieldSubmitted: (_) => controller.sendOTP(), ); } Widget _buildSendOTPButton(ThemeData theme) { final isDisabled = controller.isSending.value || controller.timer.value > 0; return Align( alignment: Alignment.center, child: MyButton.rounded( onPressed: isDisabled ? null : controller.sendOTP, elevation: 2, padding: MySpacing.xy(24, 16), borderRadiusAll: 10, backgroundColor: isDisabled ? Colors.grey : contentTheme.brandRed, child: controller.isSending.value ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: theme.colorScheme.onPrimary, ), ) : MyText.labelMedium( 'Send OTP', fontWeight: 600, color: theme.colorScheme.onPrimary, ), ), ); } Widget _buildOTPSentInfo() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(Icons.sms_outlined, color: Colors.redAccent, size: 24), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodySmall( 'OTP sent to', fontWeight: 500, ), MyText.bodySmall( '+91-${controller.phoneNumber.value}', fontWeight: 700, color: Colors.black, ), ], ), ), TextButton.icon( onPressed: controller.resetForChangeNumber, icon: Icon(Icons.edit, size: 16, color: Colors.redAccent), label: MyText.bodySmall( 'Change', fontWeight: 600, color: Colors.redAccent, ), style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), foregroundColor: Colors.redAccent, ), ), ], ), ); } Widget _buildOTPForm(ThemeData theme) { return Form( key: _otpFormKey, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( 4, (index) => _buildOTPBox(index: index, theme: theme)), ), ); } Widget _buildOTPBox({required int index, required ThemeData theme}) { return Container( margin: MySpacing.x(6), width: 50, child: TextFormField( controller: controller.otpControllers[index], focusNode: controller.focusNodes[index], maxLength: 1, textAlign: TextAlign.center, style: MyTextStyle.titleLarge(fontWeight: 700), keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], onChanged: (value) => controller.onOTPChanged(value, index), decoration: InputDecoration( counterText: '', filled: true, fillColor: Colors.grey.shade100, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade400, width: 1), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: contentTheme.brandRed, width: 2), ), ), ), ); } Widget _buildResendOTPSection(ThemeData theme) { if (controller.timer.value > 0) { return MyText.bodySmall( 'Resend OTP in ${controller.timer.value}s', color: theme.colorScheme.onBackground.withOpacity(0.6), textAlign: TextAlign.center, ); } else { return Align( alignment: Alignment.center, child: TextButton( onPressed: controller.isResending.value ? null : controller.onResendOTP, child: controller.isResending.value ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : MyText.bodySmall( 'Resend OTP', fontWeight: 600, color: Colors.blueAccent, ), ), ); } } Widget _buildVerifyOTPButton(ThemeData theme) { return Align( alignment: Alignment.center, child: MyButton.rounded( onPressed: controller.verifyOTP, elevation: 2, padding: MySpacing.xy(24, 16), borderRadiusAll: 10, backgroundColor: contentTheme.brandRed, child: MyText.labelMedium( 'Verify OTP', fontWeight: 600, color: theme.colorScheme.onPrimary, ), ), ); } }