import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'dart:math' as math; import '../theme/app_theme.dart'; import '../models/cycle_entry.dart'; <<<<<<< HEAD class CycleRing extends StatelessWidget { ======= class CycleRing extends StatefulWidget { >>>>>>> 6742220 (Your commit message here) final int dayOfCycle; final int totalDays; final CyclePhase phase; const CycleRing({ super.key, required this.dayOfCycle, required this.totalDays, required this.phase, }); @override <<<<<<< HEAD Widget build(BuildContext context) { final progress = dayOfCycle / totalDays; final daysUntilNextPeriod = totalDays - dayOfCycle; return Container( width: 220, height: 220, child: Stack( alignment: Alignment.center, children: [ // Background ring CustomPaint( size: const Size(220, 220), painter: _CycleRingPainter( progress: progress, phase: phase, ), ), // Center content Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Day $dayOfCycle', style: GoogleFonts.outfit( fontSize: 32, fontWeight: FontWeight.w600, color: AppColors.charcoal, ), ), const SizedBox(height: 4), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: _getPhaseColor(phase).withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( phase.emoji, style: const TextStyle(fontSize: 14), ), const SizedBox(width: 6), Text( phase.label, style: GoogleFonts.outfit( fontSize: 14, fontWeight: FontWeight.w500, color: _getPhaseColor(phase), ), ), ], ), ), const SizedBox(height: 8), Text( daysUntilNextPeriod > 0 ? '$daysUntilNextPeriod days until period' : 'Period expected', style: GoogleFonts.outfit( fontSize: 12, color: AppColors.warmGray, ======= State createState() => _CycleRingState(); } class _CycleRingState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), ); _animation = CurvedAnimation( parent: _controller, curve: Curves.easeOutCubic, ); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final targetProgress = widget.dayOfCycle / widget.totalDays; final daysUntilNextPeriod = widget.totalDays - widget.dayOfCycle; final isDark = Theme.of(context).brightness == Brightness.dark; return AnimatedBuilder( animation: _animation, builder: (context, child) { final currentProgress = targetProgress * _animation.value; return SizedBox( width: 220, height: 220, child: Stack( alignment: Alignment.center, children: [ // Background ring CustomPaint( size: const Size(220, 220), painter: _CycleRingPainter( progress: currentProgress, phase: widget.phase, isDark: isDark, ), ), // Center content with scale and fade animation Transform.scale( scale: 0.8 + (0.2 * _animation.value), child: Opacity( opacity: _animation.value, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Day ${widget.dayOfCycle}', style: Theme.of(context).textTheme.displayMedium?.copyWith( fontSize: 32, fontWeight: FontWeight.w600, color: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(height: 4), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: _getPhaseColor(widget.phase).withOpacity(isDark ? 0.3 : 0.2), borderRadius: BorderRadius.circular(20), border: isDark ? Border.all(color: _getPhaseColor(widget.phase).withOpacity(0.5)) : null, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( widget.phase.emoji, style: const TextStyle(fontSize: 14), ), const SizedBox(width: 6), Text( widget.phase.label, style: GoogleFonts.outfit( fontSize: 14, fontWeight: FontWeight.w500, color: isDark ? Colors.white : _getPhaseColor(widget.phase), ), ), ], ), ), const SizedBox(height: 8), Text( daysUntilNextPeriod > 0 ? '$daysUntilNextPeriod days until period' : 'Period expected', style: Theme.of(context).textTheme.bodySmall?.copyWith( fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ), >>>>>>> 6742220 (Your commit message here) ), ), ], ), <<<<<<< HEAD ], ), ======= ); }, >>>>>>> 6742220 (Your commit message here) ); } Color _getPhaseColor(CyclePhase phase) { switch (phase) { case CyclePhase.menstrual: return AppColors.menstrualPhase; case CyclePhase.follicular: return AppColors.follicularPhase; case CyclePhase.ovulation: return AppColors.ovulationPhase; case CyclePhase.luteal: return AppColors.lutealPhase; } } } class _CycleRingPainter extends CustomPainter { final double progress; final CyclePhase phase; <<<<<<< HEAD _CycleRingPainter({required this.progress, required this.phase}); ======= final bool isDark; _CycleRingPainter({ required this.progress, required this.phase, required this.isDark, }); >>>>>>> 6742220 (Your commit message here) @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final radius = size.width / 2 - 15; const strokeWidth = 12.0; // Background arc final bgPaint = Paint() <<<<<<< HEAD ..color = AppColors.lightGray.withOpacity(0.2) ======= ..color = (isDark ? Colors.white : AppColors.lightGray).withOpacity(isDark ? 0.05 : 0.1) >>>>>>> 6742220 (Your commit message here) ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round; canvas.drawCircle(center, radius, bgPaint); // Progress arc final progressPaint = Paint() ..shader = SweepGradient( startAngle: -math.pi / 2, endAngle: math.pi * 1.5, colors: _getGradientColors(phase), stops: const [0.0, 0.5, 1.0], ).createShader(Rect.fromCircle(center: center, radius: radius)) ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), -math.pi / 2, 2 * math.pi * progress, false, progressPaint, ); // Dot at current position final dotAngle = -math.pi / 2 + 2 * math.pi * progress; final dotX = center.dx + radius * math.cos(dotAngle); final dotY = center.dy + radius * math.sin(dotAngle); final dotPaint = Paint() <<<<<<< HEAD ..color = Colors.white ======= ..color = isDark ? const Color(0xFF1E1E1E) : Colors.white >>>>>>> 6742220 (Your commit message here) ..style = PaintingStyle.fill; final dotBorderPaint = Paint() ..color = _getPhaseColor(phase) ..style = PaintingStyle.stroke ..strokeWidth = 3; canvas.drawCircle(Offset(dotX, dotY), 8, dotPaint); canvas.drawCircle(Offset(dotX, dotY), 8, dotBorderPaint); } List _getGradientColors(CyclePhase phase) { switch (phase) { case CyclePhase.menstrual: return [AppColors.rose, AppColors.menstrualPhase, AppColors.blushPink]; case CyclePhase.follicular: return [AppColors.sageGreen, AppColors.follicularPhase, AppColors.sageGreen.withOpacity(0.7)]; case CyclePhase.ovulation: return [AppColors.lavender, AppColors.ovulationPhase, AppColors.rose]; case CyclePhase.luteal: return [AppColors.lutealPhase, AppColors.lavender, AppColors.blushPink]; } } Color _getPhaseColor(CyclePhase phase) { switch (phase) { case CyclePhase.menstrual: return AppColors.menstrualPhase; case CyclePhase.follicular: return AppColors.follicularPhase; case CyclePhase.ovulation: return AppColors.ovulationPhase; case CyclePhase.luteal: return AppColors.lutealPhase; } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }