import 'package:flutter/material.dart'; import 'verification_code_input.dart'; const Color primaryBrown = Color(0xFF4A3C3C); /// Complete verification step widget with OTP code input class VerificationStepWidget extends StatelessWidget { /// Controllers for the 5-digit code input final List codeControllers; /// Whether code verification failed final bool isError; /// Callback when error should be reset (user starts typing) final VoidCallback? onErrorReset; /// Remaining seconds for timer (0 = show resend button) final int remainingSeconds; /// Formatted time string (e.g., "3.25") final String formattedTime; /// Loading state final bool isLoading; /// Callback when verify button pressed final VoidCallback? onVerify; /// Callback when resend code button pressed final VoidCallback? onResend; /// Callback when "Ganti Email" button pressed final VoidCallback? onChangeEmail; /// Image path for normal state final String normalImagePath; /// Image path for error state final String errorImagePath; /// Builder for the verify button (allows custom gradient button) final Widget Function(String text, VoidCallback? onPressed, bool isLoading) buttonBuilder; const VerificationStepWidget({ super.key, required this.codeControllers, required this.isError, this.onErrorReset, required this.remainingSeconds, required this.formattedTime, required this.isLoading, this.onVerify, this.onResend, this.onChangeEmail, this.normalImagePath = 'assets/images/registerletter1.gif', this.errorImagePath = 'assets/images/registerletter2.gif', required this.buttonBuilder, }); @override Widget build(BuildContext context) { return Column( children: [ // Animated Image based on error state Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: Image.asset( isError ? errorImagePath : normalImagePath, key: ValueKey(isError), height: 180, fit: BoxFit.contain, ), ), ), // Timer or Resend Button if (remainingSeconds > 0) Text( formattedTime, style: const TextStyle( fontFamily: 'Poppins', fontSize: 16, fontWeight: FontWeight.w600, color: primaryBrown, ), ) else TextButton.icon( onPressed: isLoading ? null : onResend, icon: const Icon(Icons.refresh, color: primaryBrown), label: const Text( 'Kirim Ulang Kode', style: TextStyle( fontFamily: 'Poppins', fontWeight: FontWeight.w600, color: primaryBrown, ), ), ), const SizedBox(height: 16), // Instruction text const Text( 'Masukkan kode verifikasi yang dikirim ke email Anda', textAlign: TextAlign.center, style: TextStyle( fontFamily: 'Poppins', fontSize: 14, color: primaryBrown, ), ), const SizedBox(height: 24), // Code Input VerificationCodeInput( controllers: codeControllers, isError: isError, onErrorReset: onErrorReset, enabled: !isLoading, ), // Error Message if (isError) ...[ const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: Colors.red.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Text( code.length < 5 ? 'Masukkan 5 digit kode verifikasi' : 'Kode verifikasi salah atau sudah expired!', textAlign: TextAlign.center, style: const TextStyle( color: Colors.red, fontSize: 12, fontFamily: 'Poppins', ), ), ), ], const SizedBox(height: 30), // Verify Button buttonBuilder('Verifikasi', isLoading ? null : onVerify, isLoading), // Change Email Button if (onChangeEmail != null) ...[ const SizedBox(height: 16), TextButton( onPressed: onChangeEmail, child: const Text( 'Ganti Metode', style: TextStyle(color: Colors.grey, fontFamily: 'Poppins'), ), ), ], ], ); } /// Get the combined code from all controllers String get code => codeControllers.map((c) => c.text).join(); /// Check if all 5 digits are entered bool get isComplete => code.length == 5; }