proxybuy-flutter/lib/screens/mains/hunt/widgets/hunt_timer_widget.dart

260 lines
7.3 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:lba/gen/assets.gen.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<HuntTimerWidget> createState() => _HuntTimerWidgetState();
}
class _HuntTimerWidgetState extends State<HuntTimerWidget>
with TickerProviderStateMixin {
Timer? _timer;
Duration _currentTime = Duration.zero;
late AnimationController _pulseController;
late Animation<double> _pulseAnimation;
@override
void initState() {
super.initState();
_currentTime = widget.timeRemaining;
_pulseController = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_pulseAnimation = Tween<double>(
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);
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: [
SvgPicture.asset(
isExpired
? Assets.icons.timerHunt.path
: _currentTime.inMinutes < 2
? Assets.icons.timerHunt.path
: Assets.icons.timerHunt.path,
color: timerColor,
width: widget.fontSize != null ? widget.fontSize! + 4 : 16,
),
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<CountdownProgressWidget> createState() => _CountdownProgressWidgetState();
}
class _CountdownProgressWidgetState extends State<CountdownProgressWidget>
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),
),
),
),
);
},
);
}
}