import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:qpay/screens/onboarding/verify/verify_controller.dart'; import 'package:qpay/screens/onboarding/widgets/onboarding_layout.dart'; import 'package:skeletonizer/skeletonizer.dart'; class VerifyScreen extends StatefulWidget { const VerifyScreen({super.key}); @override State createState() => _VerifyScreenState(); } class _VerifyScreenState extends State { late VerifyController verifyController; String? errorMessage; @override void initState() { super.initState(); verifyController = VerifyController(context); } final List _codeControllers = List.generate( 6, (index) => TextEditingController(), ); final List _focusNodes = List.generate(6, (index) => FocusNode()); @override void dispose() { for (var controller in _codeControllers) { controller.dispose(); } for (var node in _focusNodes) { node.dispose(); } super.dispose(); } void showErrorMessage(String? message) { setState(() { errorMessage = message; }); } void _onCodeChanged(String value, int index) { if (value.length == 1 && index < 5) { _focusNodes[index + 1].requestFocus(); } else if (value.isEmpty && index > 0) { _focusNodes[index - 1].requestFocus(); } } Future _submitCode() async { showErrorMessage(null); String code = ''; for (var controller in _codeControllers) { code += controller.text; } if (code.length != 6) { showErrorMessage('Invalid code'); return; } verifyController.updateCode(code); final response = await verifyController.verifyOtp(); if (!mounted) return; if (response.isSuccess) { if (verifyController.model.status == 'done') { context.go('/onboarding/complete'); } else { showErrorMessage( verifyController.model.errorMessage ?? 'Verification failed', ); } } else { showErrorMessage( response.error ?? 'Verification failed. Please try again.', ); } } @override Widget build(BuildContext context) { return ListenableBuilder( listenable: verifyController, builder: (context, child) { return OnboardingLayout( showBackButton: true, trailingLabel: 'Sign in', trailingRoute: '/onboarding/login', trailingIcon: Icons.login_rounded, bottomLinkLabel: 'Continue as guest?', bottomLinkRoute: '/', child: OnboardingCard( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const OnboardingHero( icon: Icons.sms_rounded, title: 'Verify your account', subtitle: 'Enter the 6-digit code we sent to your phone or email.', ), const SizedBox(height: 32), _buildCodeInputs(), const SizedBox(height: 16), if (errorMessage != null) Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Center( child: Text( errorMessage!, style: TextStyle( color: Theme.of(context).colorScheme.error, fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ), const SizedBox(height: 8), _buildResendRow(), const SizedBox(height: 28), Skeletonizer( enabled: verifyController.model.isLoading, child: OnboardingPrimaryButton( label: 'Verify code', icon: Icons.check_rounded, onPressed: _submitCode, ), ), ], ), ), ); }, ); } Widget _buildCodeInputs() { final colorScheme = Theme.of(context).colorScheme; final borderRadius = BorderRadius.circular(14); return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: List.generate(6, (index) { return SizedBox( width: 48, child: TextFormField( controller: _codeControllers[index], focusNode: _focusNodes[index], textAlign: TextAlign.center, keyboardType: TextInputType.number, maxLength: 1, style: TextStyle( fontSize: 22, fontWeight: FontWeight.w700, color: colorScheme.onSurface, ), decoration: InputDecoration( counterText: '', filled: true, fillColor: colorScheme.surface, contentPadding: const EdgeInsets.symmetric(vertical: 14), enabledBorder: OutlineInputBorder( borderRadius: borderRadius, borderSide: BorderSide( color: colorScheme.onSurface.withValues(alpha: 0.12), ), ), focusedBorder: OutlineInputBorder( borderRadius: borderRadius, borderSide: BorderSide( color: colorScheme.primary, width: 1.6, ), ), errorBorder: OutlineInputBorder( borderRadius: borderRadius, borderSide: BorderSide(color: colorScheme.error), ), focusedErrorBorder: OutlineInputBorder( borderRadius: borderRadius, borderSide: BorderSide( color: colorScheme.error, width: 1.6, ), ), ), onChanged: (value) => _onCodeChanged(value, index), ), ); }), ); } Widget _buildResendRow() { final colorScheme = Theme.of(context).colorScheme; return Center( child: Wrap( crossAxisAlignment: WrapCrossAlignment.center, children: [ Text( "Didn't receive the code?", style: TextStyle( color: colorScheme.onSurface.withValues(alpha: 0.65), fontSize: 14, ), ), TextButton( onPressed: () { verifyController.resendOtp(); }, style: TextButton.styleFrom( foregroundColor: colorScheme.primary, padding: const EdgeInsets.symmetric(horizontal: 6), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w700, ), ), child: const Text('Resend code'), ), ], ), ); } }