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

293 lines
10 KiB
Dart

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../models/drink_type.dart';
import '../services/hydration_service.dart';
import '../widgets/fluid_sphere_button.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final hydration = context.watch<HydrationService>();
final dynamicColor = hydration.dynamicWaterColor;
final size = MediaQuery.of(context).size;
return Scaffold(
body: Stack(
children: [
Positioned(
top: -100,
left: -100,
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 100, sigmaY: 100),
child: Container(
width: 450,
height: 450,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: dynamicColor.withOpacity(0.15),
),
),
),
),
Positioned(
top: size.height * 0.3,
right: -100,
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 80, sigmaY: 80),
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.purpleAccent.withOpacity(0.05),
),
),
),
),
SafeArea(
child: Column(
children: [
const SizedBox(height: 10),
_buildModernHeader(context, hydration),
Expanded(
child: Center(
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 300,
height: 300,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: dynamicColor.withOpacity(0.05),
width: 1,
)
),
),
FluidSphereButton(
percentage: hydration.percentage,
waterColor: dynamicColor,
onLongPressStart: () => HapticFeedback.heavyImpact(),
onLongPressEnd: () {
context.read<HydrationService>().addWater(200);
HapticFeedback.mediumImpact();
},
),
],
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: Text(
hydration.statusText,
key: ValueKey(hydration.statusText),
style: GoogleFonts.montserrat(
color: dynamicColor.withOpacity(0.8),
fontSize: 13,
letterSpacing: 3.0,
fontWeight: FontWeight.w600
),
),
),
),
_buildFloatingDock(context, hydration),
const SizedBox(height: 110),
],
),
),
],
),
);
}
Widget _buildModernHeader(BuildContext context, HydrationService service) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
DateFormat('MMM d, EEEE').format(DateTime.now()).toUpperCase(),
style: GoogleFonts.montserrat(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.grey.shade400,
letterSpacing: 1.5,
),
),
const SizedBox(height: 8),
RichText(
text: TextSpan(
children: [
TextSpan(
text: "${(service.percentage * 100).toInt()}",
style: GoogleFonts.montserrat(
color: Colors.black87,
fontSize: 64,
fontWeight: FontWeight.w200,
height: 1.0,
),
),
TextSpan(
text: "%",
style: GoogleFonts.montserrat(
color: Colors.black54,
fontSize: 24,
fontWeight: FontWeight.w300,
fontFeatures: [const FontFeature.superscripts()],
),
),
],
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 10,
offset: const Offset(0, 4)
)
]
),
child: IconButton(
icon: Icon(Icons.refresh, size: 20, color: service.dynamicWaterColor),
onPressed: () {},
),
),
const SizedBox(height: 12),
Text(
"TARGET",
style: GoogleFonts.montserrat(fontSize: 10, color: Colors.grey.shade400, letterSpacing: 1),
),
Text(
"${service.dailyGoal.toInt()} ml",
style: GoogleFonts.lato(fontSize: 14, color: Colors.black87, fontWeight: FontWeight.bold),
),
],
),
],
),
],
),
);
}
Widget _buildFloatingDock(BuildContext context, HydrationService service) {
return Container(
height: 85,
margin: const EdgeInsets.symmetric(horizontal: 24),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.4),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: Colors.white.withOpacity(0.4)),
boxShadow: [
BoxShadow(
color: service.dynamicWaterColor.withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: DrinkType.values.map((type) {
final isWater = type == DrinkType.Water;
return GestureDetector(
onTap: () {
context.read<HydrationService>().addWater(250, type: type);
HapticFeedback.lightImpact();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Recorded ${type.label}"),
backgroundColor: context.read<HydrationService>().dynamicWaterColor,
duration: const Duration(milliseconds: 800),
behavior: SnackBarBehavior.floating,
width: 200,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
)
);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isWater ? Colors.white.withOpacity(0.8) : Colors.transparent,
shape: BoxShape.circle,
boxShadow: isWater ? [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 4))
] : null,
),
child: Icon(
_getIconForType(type),
color: isWater ? const Color(0xFF00BFFF) : Colors.grey.shade500,
size: 22,
),
),
const SizedBox(height: 4),
Text(
type.label,
style: GoogleFonts.montserrat(
fontSize: 10,
fontWeight: isWater ? FontWeight.w600 : FontWeight.w400,
color: isWater ? Colors.black87 : Colors.grey.shade600
),
),
],
),
);
}).toList(),
),
),
),
);
}
IconData _getIconForType(DrinkType type) {
switch(type) {
case DrinkType.Water: return Icons.water_drop;
case DrinkType.Coffee: return Icons.coffee;
case DrinkType.Tea: return Icons.emoji_food_beverage;
case DrinkType.Soda: return Icons.local_drink;
}
}
}