import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../../../common/theme/theme.dart'; import '../../../components/button/button.dart'; @RoutePage() class PinPage extends StatefulWidget { final bool isCreatePin; // true for creating PIN, false for entering PIN final String? title; // Optional custom title const PinPage({super.key, this.isCreatePin = true, this.title}); @override State createState() => _PinPageState(); } class _PinPageState extends State { final List _controllers = List.generate( 6, (index) => TextEditingController(), ); final List _focusNodes = List.generate(6, (index) => FocusNode()); String _firstPin = ''; bool _isConfirmingPin = false; bool _isPinMismatch = false; @override void initState() { super.initState(); } void _onPinChanged(String value, int index) { if (value.isNotEmpty) { // Move to next field if (index < 5) { _focusNodes[index + 1].requestFocus(); } else { // Last field, unfocus and process PIN _focusNodes[index].unfocus(); _processPinInput(); } } else { // Handle backspace - move to previous field if (index > 0) { _focusNodes[index - 1].requestFocus(); } } } void _processPinInput() { String currentPin = _controllers .map((controller) => controller.text) .join(); if (currentPin.length == 6) { if (widget.isCreatePin) { if (!_isConfirmingPin) { // First PIN entry - store and ask for confirmation _firstPin = currentPin; setState(() { _isConfirmingPin = true; _isPinMismatch = false; }); _clearPinFields(); } else { // Confirming PIN if (currentPin == _firstPin) { // PINs match - create PIN _createPin(currentPin); } else { // PINs don't match setState(() { _isPinMismatch = true; }); _clearPinFields(); // Auto-hide error after 2 seconds Timer(const Duration(seconds: 2), () { if (mounted) { setState(() { _isPinMismatch = false; }); } }); } } } else { // Entering existing PIN _verifyPin(currentPin); } } } void _clearPinFields() { for (var controller in _controllers) { controller.clear(); } _focusNodes[0].requestFocus(); } void _createPin(String pin) { // Add your PIN creation logic here print('Creating PIN: $pin'); // Navigate to next screen or show success message } void _verifyPin(String pin) { // Add your PIN verification logic here print('Verifying PIN: $pin'); // Navigate to next screen or show error } void _resetPinCreation() { setState(() { _isConfirmingPin = false; _firstPin = ''; _isPinMismatch = false; }); _clearPinFields(); } String get _getTitle { if (widget.title != null) return widget.title!; if (widget.isCreatePin) { return _isConfirmingPin ? 'Konfirmasi PIN' : 'Buat PIN Baru'; } else { return 'Masukan PIN'; } } String get _getDescription { if (widget.isCreatePin) { if (_isConfirmingPin) { return 'Masukan kembali PIN untuk konfirmasi'; } else { return 'Buat PIN 6 digit untuk keamanan akun Anda'; } } else { return 'Masukan PIN 6 digit Anda untuk melanjutkan'; } } @override void dispose() { for (var controller in _controllers) { controller.dispose(); } for (var focusNode in _focusNodes) { focusNode.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.isCreatePin ? 'Buat PIN' : 'Masukan PIN'), leading: widget.isCreatePin && _isConfirmingPin ? IconButton( icon: Icon(Icons.arrow_back), onPressed: _resetPinCreation, ) : null, ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), // Title Text( _getTitle, style: AppStyle.xl.copyWith( fontWeight: FontWeight.w600, color: AppColor.textPrimary, ), ), const SizedBox(height: 12), // Description Text( _getDescription, style: AppStyle.sm.copyWith( color: AppColor.textSecondary, height: 1.4, ), ), // Error message for PIN mismatch if (_isPinMismatch) ...[ const SizedBox(height: 8), Text( 'PIN tidak sama. Silakan coba lagi.', style: AppStyle.sm.copyWith( color: AppColor.error, fontWeight: FontWeight.w500, height: 1.4, ), ), ], const SizedBox(height: 40), // PIN input fields Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(6, (index) { return Expanded( child: Padding( padding: EdgeInsets.only(right: index == 5 ? 0 : 8.0), child: TextFormField( controller: _controllers[index], focusNode: _focusNodes[index], keyboardType: TextInputType.number, maxLength: 1, obscureText: true, // Hide PIN input inputFormatters: [FilteringTextInputFormatter.digitsOnly], decoration: InputDecoration( counterText: '', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: _isPinMismatch ? AppColor.error : AppColor.border, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: _isPinMismatch ? AppColor.error : AppColor.primary, width: 2, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: AppColor.error), ), ), textAlign: TextAlign.center, style: AppStyle.lg.copyWith( color: AppColor.primary, fontWeight: FontWeight.w600, ), cursorColor: AppColor.primary, onChanged: (value) { setState(() { _isPinMismatch = false; }); _onPinChanged(value, index); }, ), ), ); }), ), const SizedBox(height: 40), // Progress indicator for PIN creation if (widget.isCreatePin) ...[ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( shape: BoxShape.circle, color: AppColor.primary, ), ), const SizedBox(width: 8), Container( width: 8, height: 8, decoration: BoxDecoration( shape: BoxShape.circle, color: _isConfirmingPin ? AppColor.primary : AppColor.border, ), ), ], ), const SizedBox(height: 8), Center( child: Text( _isConfirmingPin ? 'Langkah 2 dari 2' : 'Langkah 1 dari 2', style: AppStyle.xs.copyWith(color: AppColor.textSecondary), ), ), ], const Spacer(), // Continue Button AppElevatedButton( title: widget.isCreatePin ? (_isConfirmingPin ? 'Konfirmasi' : 'Lanjutkan') : 'Masuk', onPressed: () { String pin = _controllers .map((controller) => controller.text) .join(); if (pin.length == 6) { _processPinInput(); } }, ), const SizedBox(height: 24), ], ), ), ); } }