Initial commit: Sage Kitchen Management App v1.0.0
✨ Features implemented: - Smart inventory tracking with Hive database - Barcode scanning with auto-populated product info - Multiple API fallbacks (Open Food Facts, UPCItemDB) - Smart expiration date predictions by category - Discord webhook notifications (persisted) - Custom sage leaf vector icon - Material Design 3 UI with sage green theme - Privacy Policy & Terms of Service - Local-first, privacy-focused architecture 🎨 UI/UX: - Home dashboard with inventory stats - Add Item screen with barcode integration - Inventory list with expiration indicators - Settings with persistent preferences - About section with legal docs 🔧 Technical: - Flutter 3.35.5 with Riverpod state management - Hive 2.2.3 for local database - Mobile scanner for barcode detection - Feature-first architecture 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
77
lib/core/constants/app_icon.dart
Normal file
77
lib/core/constants/app_icon.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Sage leaf icon widget for use in the app
|
||||
class SageLeafIcon extends StatelessWidget {
|
||||
final double size;
|
||||
final Color? color;
|
||||
|
||||
const SageLeafIcon({
|
||||
super.key,
|
||||
this.size = 24,
|
||||
this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
size: Size(size, size),
|
||||
painter: SageLeafPainter(color: color ?? const Color(0xFF4CAF50)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SageLeafPainter extends CustomPainter {
|
||||
final Color color;
|
||||
|
||||
SageLeafPainter({required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
final path = Path();
|
||||
|
||||
// Create a simple sage leaf shape
|
||||
final centerX = size.width / 2;
|
||||
final centerY = size.height / 2;
|
||||
|
||||
// Leaf outline
|
||||
path.moveTo(centerX, size.height * 0.1);
|
||||
path.quadraticBezierTo(
|
||||
size.width * 0.8, size.height * 0.3,
|
||||
size.width * 0.85, centerY,
|
||||
);
|
||||
path.quadraticBezierTo(
|
||||
size.width * 0.8, size.height * 0.7,
|
||||
centerX, size.height * 0.9,
|
||||
);
|
||||
path.quadraticBezierTo(
|
||||
size.width * 0.2, size.height * 0.7,
|
||||
size.width * 0.15, centerY,
|
||||
);
|
||||
path.quadraticBezierTo(
|
||||
size.width * 0.2, size.height * 0.3,
|
||||
centerX, size.height * 0.1,
|
||||
);
|
||||
path.close();
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
|
||||
// Draw leaf vein
|
||||
final veinPaint = Paint()
|
||||
..color = color.withOpacity(0.6)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = size.width * 0.02;
|
||||
|
||||
final veinPath = Path();
|
||||
veinPath.moveTo(centerX, size.height * 0.1);
|
||||
veinPath.lineTo(centerX, size.height * 0.9);
|
||||
|
||||
canvas.drawPath(veinPath, veinPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
80
lib/core/constants/app_theme.dart
Normal file
80
lib/core/constants/app_theme.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'colors.dart';
|
||||
|
||||
/// App theme configuration
|
||||
class AppTheme {
|
||||
// Light Theme
|
||||
static ThemeData get lightTheme {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: AppColors.primary,
|
||||
secondary: AppColors.primaryLight,
|
||||
surface: AppColors.surface,
|
||||
error: AppColors.error,
|
||||
),
|
||||
scaffoldBackgroundColor: AppColors.background,
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
cardTheme: CardThemeData(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Dark Theme (for future)
|
||||
static ThemeData get darkTheme {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: AppColors.primaryLight,
|
||||
secondary: AppColors.primary,
|
||||
surface: const Color(0xFF1E1E1E),
|
||||
error: AppColors.error,
|
||||
),
|
||||
scaffoldBackgroundColor: const Color(0xFF121212),
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: Color(0xFF1E1E1E),
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
37
lib/core/constants/colors.dart
Normal file
37
lib/core/constants/colors.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// App color palette
|
||||
class AppColors {
|
||||
// Primary - Sage Green theme 🌿
|
||||
static const primary = Color(0xFF4CAF50);
|
||||
static const primaryDark = Color(0xFF388E3C);
|
||||
static const primaryLight = Color(0xFF81C784);
|
||||
|
||||
// Expiration Status Colors
|
||||
static const fresh = Color(0xFF4CAF50); // Green
|
||||
static const caution = Color(0xFFFFEB3B); // Yellow
|
||||
static const warning = Color(0xFFFF9800); // Orange
|
||||
static const critical = Color(0xFFF44336); // Red
|
||||
static const expired = Color(0xFF9E9E9E); // Gray
|
||||
|
||||
// UI Colors
|
||||
static const background = Color(0xFFFAFAFA);
|
||||
static const surface = Color(0xFFFFFFFF);
|
||||
static const text = Color(0xFF212121);
|
||||
static const textSecondary = Color(0xFF757575);
|
||||
static const divider = Color(0xFFBDBDBD);
|
||||
|
||||
// Semantic Colors
|
||||
static const success = Color(0xFF4CAF50);
|
||||
static const error = Color(0xFFF44336);
|
||||
static const info = Color(0xFF2196F3);
|
||||
static const warning2 = Color(0xFFFF9800);
|
||||
|
||||
// Location Colors (subtle backgrounds)
|
||||
static const fridgeColor = Color(0xFFE3F2FD); // Light blue
|
||||
static const freezerColor = Color(0xFFE1F5FE); // Lighter blue
|
||||
static const pantryColor = Color(0xFFFFF9C4); // Light yellow
|
||||
static const spiceRackColor = Color(0xFFFFECB3); // Light amber
|
||||
static const countertopColor = Color(0xFFE8F5E9); // Light green
|
||||
static const otherColor = Color(0xFFF5F5F5); // Light gray
|
||||
}
|
Reference in New Issue
Block a user