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'; class CycleRing extends StatelessWidget { final int dayOfCycle; final int totalDays; final CyclePhase phase; const CycleRing({ super.key, required this.dayOfCycle, required this.totalDays, required this.phase, }); @override 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, ), ), ], ), ], ), ); } 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; _CycleRingPainter({required this.progress, required this.phase}); @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() ..color = AppColors.lightGray.withOpacity(0.2) ..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() ..color = Colors.white ..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; }