import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../core/app_colors.dart'; class DietPalette { DietPalette._(); static const primary = Color(0xFF6476E8); static const primarySoft = Color(0xFFEEF0FF); static const protein = Color(0xFF38A77B); static const carbs = Color(0xFF4E91D8); static const fat = Color(0xFFE58A76); static const calorie = Color(0xFF6D6FEA); static const gradient = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF8FA7FF), primary], ); } class DietCalorieRing extends StatelessWidget { final int calories; final double size; const DietCalorieRing({super.key, required this.calories, this.size = 116}); @override Widget build(BuildContext context) { final progress = (calories / 800).clamp(0.04, 1.0); return Semantics( label: '总热量 $calories 千卡', child: SizedBox.square( dimension: size, child: CustomPaint( painter: _CalorieRingPainter(progress), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '$calories', style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w800, color: AppColors.textPrimary, ), ), const Text( '千卡', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: AppColors.textHint, ), ), ], ), ), ), ); } } class DietMacroBar extends StatelessWidget { final String label; final int value; final Color color; final double reference; const DietMacroBar({ super.key, required this.label, required this.value, required this.color, required this.reference, }); @override Widget build(BuildContext context) { final progress = (value / reference).clamp(0.0, 1.0); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( label, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textSecondary, ), ), const Spacer(), Text( '$value g', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), ], ), const SizedBox(height: 6), ClipRRect( borderRadius: BorderRadius.circular(999), child: LinearProgressIndicator( minHeight: 5, value: progress, backgroundColor: color.withValues(alpha: 0.12), valueColor: AlwaysStoppedAnimation(color), ), ), ], ); } } class _CalorieRingPainter extends CustomPainter { final double progress; const _CalorieRingPainter(this.progress); @override void paint(Canvas canvas, Size size) { final center = size.center(Offset.zero); final radius = size.shortestSide / 2 - 8; final track = Paint() ..color = DietPalette.primarySoft ..style = PaintingStyle.stroke ..strokeWidth = 9 ..strokeCap = StrokeCap.round; final progressPaint = Paint() ..shader = const LinearGradient( colors: [Color(0xFF8FA7FF), DietPalette.calorie], ).createShader(Offset.zero & size) ..style = PaintingStyle.stroke ..strokeWidth = 9 ..strokeCap = StrokeCap.round; canvas.drawCircle(center, radius, track); canvas.drawArc( Rect.fromCircle(center: center, radius: radius), -math.pi / 2, math.pi * 2 * progress, false, progressPaint, ); } @override bool shouldRepaint(covariant _CalorieRingPainter oldDelegate) => oldDelegate.progress != progress; }