import 'dart:async'; import 'package:flutter/material.dart'; import 'package:lba/res/colors.dart'; class HuntTimerWidget extends StatefulWidget { final Duration timeRemaining; final bool isActive; final bool showMinutesSeconds; final TextStyle? textStyle; final double? fontSize; const HuntTimerWidget({ super.key, required this.timeRemaining, this.isActive = false, this.showMinutesSeconds = false, this.textStyle, this.fontSize, }); @override State createState() => _HuntTimerWidgetState(); } class _HuntTimerWidgetState extends State with TickerProviderStateMixin { Timer? _timer; Duration _currentTime = Duration.zero; late AnimationController _pulseController; late Animation _pulseAnimation; @override void initState() { super.initState(); _currentTime = widget.timeRemaining; _pulseController = AnimationController( duration: const Duration(seconds: 1), vsync: this, ); _pulseAnimation = Tween( begin: 1.0, end: 1.1, ).animate(CurvedAnimation( parent: _pulseController, curve: Curves.easeInOut, )); if (widget.isActive) { _startTimer(); } } @override void didUpdateWidget(HuntTimerWidget oldWidget) { super.didUpdateWidget(oldWidget); if (widget.timeRemaining != oldWidget.timeRemaining) { _currentTime = widget.timeRemaining; } if (widget.isActive != oldWidget.isActive) { if (widget.isActive) { _startTimer(); } else { _stopTimer(); } } } void _startTimer() { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (mounted) { setState(() { if (_currentTime.inSeconds > 0) { _currentTime = Duration(seconds: _currentTime.inSeconds - 1); // Pulse animation when time is running low if (_currentTime.inMinutes < 5) { _pulseController.forward().then((_) { _pulseController.reverse(); }); } } else { _stopTimer(); } }); } }); } void _stopTimer() { _timer?.cancel(); _timer = null; } @override void dispose() { _timer?.cancel(); _pulseController.dispose(); super.dispose(); } String _formatTime() { if (widget.showMinutesSeconds) { final minutes = _currentTime.inMinutes; final seconds = _currentTime.inSeconds % 60; return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; } else { final hours = _currentTime.inHours; final minutes = _currentTime.inMinutes % 60; final seconds = _currentTime.inSeconds % 60; return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; } } Color _getTimerColor() { if (_currentTime.inMinutes < 2) { return Colors.red; } else if (_currentTime.inMinutes < 5) { return Colors.orange; } else if (_currentTime.inHours < 2) { return AppColors.offerTimer; } else { return AppColors.confirmButton; } } @override Widget build(BuildContext context) { final timerColor = _getTimerColor(); final isExpired = _currentTime.inSeconds <= 0; return AnimatedBuilder( animation: _pulseAnimation, builder: (context, child) { return Transform.scale( scale: _currentTime.inMinutes < 5 && widget.isActive ? _pulseAnimation.value : 1.0, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: timerColor.withOpacity(0.15), borderRadius: BorderRadius.circular(12), border: Border.all( color: timerColor.withOpacity(0.4), width: 1, ), boxShadow: _currentTime.inMinutes < 5 && widget.isActive ? [ BoxShadow( color: timerColor.withOpacity(0.3), blurRadius: 8, spreadRadius: 1, ), ] : null, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isExpired ? Icons.timer_off_outlined : _currentTime.inMinutes < 2 ? Icons.timer_outlined : Icons.timer_rounded, color: timerColor, size: widget.fontSize != null ? widget.fontSize! + 2 : 14, ), const SizedBox(width: 4), Text( isExpired ? 'Expired' : _formatTime(), style: widget.textStyle?.copyWith( color: timerColor, fontWeight: FontWeight.bold, fontFamily: 'monospace', ) ?? TextStyle( color: timerColor, fontWeight: FontWeight.bold, fontSize: widget.fontSize ?? 10, fontFamily: 'monospace', ), ), ], ), ), ); }, ); } } class CountdownProgressWidget extends StatefulWidget { final Duration timeRemaining; final Duration totalTime; const CountdownProgressWidget({ super.key, required this.timeRemaining, this.totalTime = const Duration(hours: 12), }); @override State createState() => _CountdownProgressWidgetState(); } class _CountdownProgressWidgetState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); _animationController.forward(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final progress = widget.timeRemaining.inSeconds / widget.totalTime.inSeconds; final clampedProgress = progress.clamp(0.0, 1.0); return AnimatedBuilder( animation: _animationController, builder: (context, child) { return Container( height: 8, decoration: BoxDecoration( color: AppColors.divider.withOpacity(0.3), borderRadius: BorderRadius.circular(4), ), child: FractionallySizedBox( alignment: Alignment.centerLeft, widthFactor: clampedProgress * _animationController.value, child: Container( decoration: BoxDecoration( color: clampedProgress > 0.2 ? AppColors.confirmButton : AppColors.offerTimer, borderRadius: BorderRadius.circular(4), ), ), ), ); }, ); } }