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'; import 'package:marco/helpers/widgets/my_snackbar.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 SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodyMedium('OTP Verification', fontWeight: 600), MySpacing.height(24), Obx(() => !controller.isOTPSent.value ? Column( children: [ _buildEmailInput(), MySpacing.height(24), _buildSendOTPButton(theme), ], ) : Column( children: [ _buildOTPSentInfo(), MySpacing.height(12), _buildOTPForm(theme), MySpacing.height(12), _buildResendOTPSection(theme), MySpacing.height(12), _buildVerifyOTPButton(theme), ], )), ], ), ); } Widget _buildEmailInput() { return TextFormField( controller: controller.emailController, keyboardType: TextInputType.emailAddress, decoration: InputDecoration( labelText: 'Enter Email Address', border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)), ), 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.primary, 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.email_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(controller.email.value, fontWeight: 700, color: Colors.black), ], ), ), TextButton.icon( onPressed: controller.resetForChangeEmail, icon: Icon(Icons.edit, size: 16, color: Colors.redAccent), label: MyText.bodySmall('Change', fontWeight: 600, color: 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), validator: (value) { if (value == null || value.isEmpty) return ''; return null; }, 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: () { if (_otpFormKey.currentState!.validate()) { controller.verifyOTP(); } else { showAppSnackbar( title: "Error", message: "Please enter all 4 digits of the OTP", type: SnackbarType.error, ); } }, elevation: 2, padding: MySpacing.xy(24, 16), borderRadiusAll: 10, backgroundColor: contentTheme.brandRed, child: MyText.labelMedium( 'Verify OTP', fontWeight: 600, color: theme.colorScheme.onPrimary, ), ), ); } }