Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,45 +26,47 @@ class MyApp extends StatelessWidget {
themeMode: provider.themeMode,
theme: ThemeData(
useMaterial3: true,
scaffoldBackgroundColor: const Color(0xFFEFF1F1), // Light Grey
scaffoldBackgroundColor: const Color(0xFFF5F1ED), // Warm cream background
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF253ABD), // Primary Blue
seedColor: const Color(0xFF6F4E37), // Coffee brown
brightness: Brightness.light,
primary: const Color(0xFF253ABD),
secondary: const Color(0xFFE5E5EA), // Light Grey for elements
surface: const Color(0xFFFFFFFF), // White for cards
primary: const Color(0xFF6F4E37), // Coffee brown
secondary: const Color(0xFFE5DDD5), // Warm light beige
surface: const Color(0xFFFFFBF7), // Warm white for cards
onPrimary: const Color(0xFFFFFFFF),
onSurface: const Color(0xFF000000),
onSurface: const Color(0xFF2B1F1A), // Dark brown text
tertiary: const Color(0xFFD2691E), // Warm amber accent
),
textTheme: _buildTextTheme(Brightness.light),
appBarTheme: AppBarTheme(
backgroundColor: const Color(0xFFEFF1F1),
foregroundColor: Colors.black,
backgroundColor: const Color(0xFFF5F1ED),
foregroundColor: const Color(0xFF2B1F1A),
elevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black,
color: const Color(0xFF2B1F1A),
),
),
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: Color(0xFF253ABD),
backgroundColor: Color(0xFF6F4E37), // Coffee brown
foregroundColor: Colors.white,
),
),
darkTheme: ThemeData(
useMaterial3: true,
scaffoldBackgroundColor: const Color(0xFF000000), // Black
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFFF9F0A), // Technical Orange
seedColor: const Color(0xFFD2691E), // Warm amber (chocolate)
brightness: Brightness.dark,
primary: const Color(0xFFFF9F0A),
primary: const Color(0xFFD2691E), // Warm amber
secondary: const Color(0xFF3A3A3C), // Dark Grey for elements
surface: const Color(0xFF1C1C1E), // Slightly lighter grey for cards
onPrimary: const Color(0xFF000000),
onSurface: const Color(0xFFFFFFFF),
tertiary: const Color(0xFFFF9F0A), // Keep technical orange as accent
),
textTheme: _buildTextTheme(Brightness.dark),
appBarTheme: AppBarTheme(
Expand All @@ -80,8 +82,8 @@ class MyApp extends StatelessWidget {
),
),
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: Color(0xFFFF9F0A),
foregroundColor: Colors.black,
backgroundColor: Color(0xFFD2691E), // Warm amber
foregroundColor: Colors.white,
),
),
home: const AppHome(),
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/add_bean_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ class _AddBeanScreenState extends State<AddBeanScreen> {
_buildLabel('NOTES'),
_buildTextField(
_notesController,
'Tasting notes, etc.',
'What makes these beans special?',
maxLines: 3,
),
const SizedBox(height: 24),
Expand Down
74 changes: 66 additions & 8 deletions lib/screens/add_shot_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ class AddShotScreen extends StatefulWidget {
}

class _AddShotScreenState extends State<AddShotScreen> {
// Encouragement messages for shot logging
static const _encouragementMessages = [
'Nice pull! 🎯',
'Another one dialed in! ☕',
'Shot logged! Keep brewing! 💪',
'Great work! ✨',
];

// Perfect shot threshold: flavor coordinates within ±0.2 of center
// (center = 0,0 meaning balanced between sour/bitter and weak/strong)
static const _perfectShotThreshold = 0.2;

final _doseInController = TextEditingController(text: '18.0');
final _doseOutController = TextEditingController(text: '36.0');
final _durationController = TextEditingController(text: '00:00.0');
Expand Down Expand Up @@ -231,6 +243,37 @@ class _AddShotScreenState extends State<AddShotScreen> {
context,
listen: false,
).addShot(widget.beanId, shot, updatePreferredGrind: _updatePreferred);

// Show encouraging message based on flavor profile
// Note: shotCount includes the newly added shot since addShot was called above
final isPerfectShot = _flavourX.abs() < _perfectShotThreshold &&
_flavourY.abs() < _perfectShotThreshold;
final shotCount = Provider.of<CoffeeProvider>(context, listen: false)
.beans
.firstWhere((b) => b.id == widget.beanId)
.shots
.length;

String message;
if (isPerfectShot) {
message = '🎯 Perfect shot! You nailed it!';
} else if (shotCount >= 10 && shotCount % 10 == 0) {
message = '🔥 ${shotCount} shots logged! You\'re on fire!';
} else {
message = _encouragementMessages[(shotCount - 1) % _encouragementMessages.length];
}

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: isPerfectShot
? Colors.green
: Theme.of(context).colorScheme.primary,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);

Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
Expand Down Expand Up @@ -605,14 +648,29 @@ class _AddShotScreenState extends State<AddShotScreen> {

// Flavour Graph
ExpansionTile(
title: Text(
'FLAVOUR PROFILE',
style: TextStyle(
fontFamily: 'RobotoMono',
fontWeight: FontWeight.bold,
fontSize: 14,
color: Theme.of(context).colorScheme.onSurface,
),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'FLAVOUR PROFILE',
style: TextStyle(
fontFamily: 'RobotoMono',
fontWeight: FontWeight.bold,
fontSize: 14,
color: Theme.of(context).colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Text(
'Tap the grid to map your shot\'s taste',
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 11,
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
fontStyle: FontStyle.italic,
),
),
],
),
children: [
const SizedBox(height: 12),
Expand Down
80 changes: 76 additions & 4 deletions lib/screens/bean_detail_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,15 @@ class _BeanDetailScreenState extends State<BeanDetailScreen> {
),
],
),
const SizedBox(height: 16),
const SizedBox(height: 24),

// Visual separator
Divider(
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
thickness: 1,
height: 1,
),
const SizedBox(height: 24),

// Flavor Wheel (Radar Chart)
_buildBentoContainer(
Expand Down Expand Up @@ -457,7 +465,15 @@ class _BeanDetailScreenState extends State<BeanDetailScreen> {
},
),
),
const SizedBox(height: 16),
const SizedBox(height: 24),

// Visual separator
Divider(
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
thickness: 1,
height: 1,
),
const SizedBox(height: 24),

// Chart Bento
if (shots.length > 1)
Expand Down Expand Up @@ -605,7 +621,42 @@ class _BeanDetailScreenState extends State<BeanDetailScreen> {
),
),
const SizedBox(height: 8),
...shots.map((shot) {
if (shots.isEmpty)
Container(
padding: const EdgeInsets.all(32),
child: Center(
child: Column(
children: [
Icon(
Icons.emoji_events_outlined,
size: 48,
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.4),
),
const SizedBox(height: 12),
Text(
'No shots logged yet',
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onSurface,
),
),
const SizedBox(height: 4),
Text(
'Pull your first! 🎯',
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 14,
color: Theme.of(context).colorScheme.primary,
),
),
],
),
),
)
else
...shots.map((shot) {
final machine = provider.machines.firstWhere(
(m) => m.id == shot.machineId,
orElse: () => CoffeeMachine(name: 'Unknown', id: ''),
Expand Down Expand Up @@ -763,13 +814,27 @@ class _BeanDetailScreenState extends State<BeanDetailScreen> {
height: height,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context).colorScheme.surface,
Theme.of(context).colorScheme.surface.withValues(alpha: 0.9),
],
),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: Theme.of(
context,
).colorScheme.onSurface.withValues(alpha: 0.05),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.06),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: child,
);
Expand All @@ -786,6 +851,13 @@ class _BeanDetailScreenState extends State<BeanDetailScreen> {
context,
).colorScheme.onSurface.withValues(alpha: 0.05),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
Expand Down
39 changes: 31 additions & 8 deletions lib/screens/bean_list_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class _BeanListScreenState extends State<BeanListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bean Vault'),
title: const Text('My Coffee Collection'),
leading: const Icon(Icons.coffee), // Aesthetic icon
actions: [
PopupMenuButton<String>(
Expand Down Expand Up @@ -98,6 +98,8 @@ class _BeanListScreenState extends State<BeanListScreen> {
},
backgroundColor: Theme.of(context).colorScheme.surface,
selectedColor: Theme.of(context).colorScheme.primary,
elevation: isSelected ? 4 : 0,
shadowColor: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
labelStyle: TextStyle(
color: isSelected ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onSurface,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
Expand All @@ -108,6 +110,7 @@ class _BeanListScreenState extends State<BeanListScreen> {
color: isSelected
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
width: isSelected ? 2 : 1,
),
),
showCheckmark: false,
Expand All @@ -125,16 +128,36 @@ class _BeanListScreenState extends State<BeanListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.coffee_maker_outlined,
size: 64,
color: Theme.of(context).colorScheme.secondary,
Icons.coffee,
size: 80,
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
),
const SizedBox(height: 16),
Text(
'No beans found',
style: TextStyle(fontFamily: 'RobotoMono',
fontSize: 18,
color: Theme.of(context).colorScheme.secondary,
'Your bean collection is empty',
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onSurface,
),
),
const SizedBox(height: 8),
Text(
'Time to stock up! ☕',
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 16,
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
),
),
const SizedBox(height: 8),
Text(
'Tap + to add your first coffee',
style: TextStyle(
fontFamily: 'RobotoMono',
fontSize: 14,
color: Theme.of(context).colorScheme.primary,
),
),
],
Expand Down
7 changes: 6 additions & 1 deletion lib/screens/gear_settings_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,12 @@ class GearSettingsScreen extends StatelessWidget {
color: Theme.of(context).colorScheme.onSurface,
),
decoration: InputDecoration(
labelText: 'Name',
labelText: isMachine ? 'Machine Name' : 'Grinder Name',
hintText: isMachine ? 'e.g., "My trusty Gaggia"' : 'e.g., "Betsy the Grinder"',
hintStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.4),
fontSize: 12,
),
labelStyle: const TextStyle(color: Colors.grey),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
Expand Down
4 changes: 2 additions & 2 deletions lib/screens/onboarding_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
),
OnboardingPage(
icon: Icons.inventory_2_outlined,
title: 'Bean Vault',
title: 'My Coffee Collection',
description:
'Store and organize your coffee bean collection with detailed information.',
details: [
Expand Down Expand Up @@ -71,7 +71,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
description:
'You\'re ready to start your coffee journey!',
details: [
'1. Tap + on Bean Vault to add your first bean',
'1. Tap + on My Coffee Collection to add your first bean',
'2. Select a bean to view details',
'3. Tap + on bean details to log a shot',
'4. Use the dial to set your grind size',
Expand Down
Loading