import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:kmobile/api/services/auth_service.dart'; import 'package:kmobile/di/injection.dart'; import 'package:kmobile/features/fund_transfer/screens/tpin_prompt_screen.dart'; import '../../../l10n/app_localizations.dart'; class TransactionPinScreen extends StatefulWidget { final Future Function(BuildContext, String) onPinCompleted; const TransactionPinScreen({super.key, required this.onPinCompleted}); @override State createState() => _TransactionPinScreenState(); } class _TransactionPinScreenState extends State with SingleTickerProviderStateMixin { final List _pin = []; bool _loading = true; bool _isSubmitting = false; late final AnimationController _bounceController; late final Animation _bounceAnimation; @override void initState() { super.initState(); _checkIfTpinIsSet(); _bounceController = AnimationController( vsync: this, duration: const Duration(milliseconds: 150), ); _bounceAnimation = Tween(begin: 1.0, end: 1.1).animate( CurvedAnimation( parent: _bounceController, curve: Curves.elasticIn, reverseCurve: Curves.elasticOut), )..addStatusListener((status) { if (status == AnimationStatus.completed) { _bounceController.reverse(); } }); } @override void dispose() { _bounceController.dispose(); super.dispose(); } Future _checkIfTpinIsSet() async { setState(() => _loading = true); try { final authService = getIt(); final isSet = await authService.checkTpin(); if (!isSet && mounted) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const TpinSetupPromptScreen()), ); } else if (mounted) { setState(() => _loading = false); } } catch (e) { if (mounted) { setState(() => _loading = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(AppLocalizations.of(context).tpinStatusFailed), ), ); } } } void _onKeyPressed(String value) { if (_isSubmitting) return; setState(() { if (value == 'back') { if (_pin.isNotEmpty) _pin.removeLast(); } else if (_pin.length < 6) { _pin.add(value); _bounceController.forward(from: 0); } }); } Future _submitPin() async { if (_isSubmitting) return; if (_pin.length == 6) { setState(() => _isSubmitting = true); try { await widget.onPinCompleted(context, _pin.join()); } finally { if (mounted) { setState(() => _isSubmitting = false); } } } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(AppLocalizations.of(context).enter6DigitTpin), ), ); } } Widget _buildPinIndicators() { return AnimatedBuilder( animation: _bounceAnimation, builder: (context, child) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(6, (index) { final isAnimating = index == _pin.length - 1 && _bounceController.isAnimating; return Transform.scale( scale: isAnimating ? _bounceAnimation.value : 1.0, child: Container( margin: const EdgeInsets.symmetric(horizontal: 8), width: 30, height: 30, decoration: BoxDecoration( shape: BoxShape.circle, color: index < _pin.length ? Theme.of(context).colorScheme.onSurfaceVariant : Theme.of(context) .colorScheme .surfaceContainerHighest), ), ); }), ); }); } Widget _buildKey(String label, {IconData? icon}) { return Expanded( child: InkWell( onTap: () { HapticFeedback.lightImpact(); if (label == 'done') { _submitPin(); } else { _onKeyPressed(label); } }, borderRadius: BorderRadius.circular(48), child: Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: Center( child: _isSubmitting && label == 'done' ? const CircularProgressIndicator() : icon != null ? Icon(icon, size: 28) : Text(label, style: const TextStyle(fontSize: 24)), ), ), ), ); } Widget _buildKeypad() { return Column( mainAxisSize: MainAxisSize.min, children: [ Row(children: [_buildKey('1'), _buildKey('2'), _buildKey('3')]), Row(children: [_buildKey('4'), _buildKey('5'), _buildKey('6')]), Row(children: [_buildKey('7'), _buildKey('8'), _buildKey('9')]), Row( children: [ _buildKey('done', icon: Icons.check), _buildKey('0'), _buildKey('back', icon: Icons.backspace_outlined), ], ), ], ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(AppLocalizations.of(context).tpin), centerTitle: false, ), body: _loading ? const Center(child: CircularProgressIndicator()) : SafeArea( child: Padding( padding: const EdgeInsets.only(bottom: 20.0), child: Column( children: [ const Spacer(), Text( AppLocalizations.of(context).enterTpin, style: const TextStyle(fontSize: 18), ), const SizedBox(height: 50), _buildPinIndicators(), const Spacer(), _buildKeypad(), ], ), ), ), ); } }