BioFlux/lib/screens/stats_screen.dart
2026-01-22 16:33:07 +08:00

155 lines
6.2 KiB
Dart

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import '../services/hydration_service.dart';
import '../widgets/glass_card.dart';
class StatsScreen extends StatelessWidget {
const StatsScreen({super.key});
@override
Widget build(BuildContext context) {
final service = context.watch<HydrationService>();
final history = service.history;
final color = service.dynamicWaterColor;
return Scaffold(
body: SafeArea(
child: ListView(
padding: const EdgeInsets.all(24.0),
children: [
Text(
"Insights",
style: GoogleFonts.montserrat(fontSize: 28, fontWeight: FontWeight.bold)
),
const SizedBox(height: 20),
Row(
children: [
_buildMetricCard("Streak", "5 Days", Icons.local_fire_department, Colors.orange),
const SizedBox(width: 12),
_buildMetricCard("Avg", "1.8 L", Icons.water, Colors.blue),
const SizedBox(width: 12),
_buildMetricCard("Goal", "90%", Icons.check_circle, Colors.green),
],
),
const SizedBox(height: 24),
Text("Intake Breakdown", style: GoogleFonts.montserrat(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
GlassCard(
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: SizedBox(
height: 12,
child: Row(
children: service.breakdown.entries.map((entry) {
if (entry.value == 0) return const SizedBox.shrink();
final flex = (entry.value * 10).toInt();
return Expanded(
flex: flex == 0 ? 1 : flex,
child: Container(color: entry.key.color),
);
}).toList(),
),
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: service.breakdown.entries.where((e) => e.value > 0).map((entry) {
return Row(
children: [
Container(width: 8, height: 8, decoration: BoxDecoration(color: entry.key.color, shape: BoxShape.circle)),
const SizedBox(width: 6),
Text(entry.key.label, style: GoogleFonts.lato(fontSize: 12, color: Colors.grey.shade700)),
],
);
}).toList(),
)
],
),
),
const SizedBox(height: 24),
Text("Weekly Overview", style: GoogleFonts.montserrat(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
AspectRatio(
aspectRatio: 1.5,
child: GlassCard(
child: Padding(
padding: const EdgeInsets.only(right: 16, top: 16, bottom: 0),
child: BarChart(
BarChartData(
gridData: const FlGridData(show: false),
titlesData: FlTitlesData(
show: true,
rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
leftTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
if (value.toInt() < days.length) {
return Text(days[value.toInt()], style: GoogleFonts.lato(fontSize: 10, color: Colors.grey));
}
return const SizedBox();
},
),
),
),
borderData: FlBorderData(show: false),
barGroups: history.asMap().entries.map((e) {
return BarChartGroupData(
x: e.key,
barRods: [
BarChartRodData(
toY: e.value,
color: e.value >= service.dailyGoal ? color : color.withOpacity(0.5),
width: 12,
borderRadius: BorderRadius.circular(6),
backDrawRodData: BackgroundBarChartRodData(
show: true,
toY: 4000,
color: Colors.grey.withOpacity(0.1),
),
),
],
);
}).toList(),
),
),
),
),
),
const SizedBox(height: 80),
],
),
),
);
}
Widget _buildMetricCard(String title, String value, IconData icon, Color color) {
return Expanded(
child: GlassCard(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 20, color: color),
const SizedBox(height: 8),
Text(value, style: GoogleFonts.montserrat(fontSize: 18, fontWeight: FontWeight.bold)),
Text(title, style: GoogleFonts.lato(fontSize: 12, color: Colors.grey.shade600)),
],
),
),
);
}
}