diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 89176ef..519054c 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,21 +1,33 @@ -allprojects { +buildscript { repositories { google() mavenCentral() } + dependencies { + classpath("com.android.tools.build:gradle:8.8.0") + classpath("com.google.gms:google-services:4.4.0") + } } -val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() -rootProject.layout.buildDirectory.value(newBuildDir) +// ✅ Set build directory +val newBuildDir = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.set(newBuildDir) +// ✅ Ensure all subprojects (like :app) use proper build directories subprojects { - val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) - project.layout.buildDirectory.value(newSubprojectBuildDir) -} -subprojects { - project.evaluationDependsOn(":app") + layout.buildDirectory.set(newBuildDir.dir(name)) + + // ✅ Add repositories so subprojects can resolve dependencies + repositories { + google() + mavenCentral() + } + + // Optional but helps ensure proper evaluation order + evaluationDependsOn(":app") } +// ✅ Clean task tasks.register("clean") { delete(rootProject.layout.buildDirectory) -} +} \ No newline at end of file diff --git a/lib/core/theme/theme.dart b/lib/core/theme/theme.dart new file mode 100644 index 0000000..77d1a5e --- /dev/null +++ b/lib/core/theme/theme.dart @@ -0,0 +1,406 @@ +import 'package:flutter/material.dart'; + +class TeaTrackerTheme { + // Primary Colors from your Figma design + static const Color primaryGreen = Color(0xFF4CAF50); // Main green from buttons + static const Color darkGreen = Color(0xFF2E7D32); // Dark green for text/borders + static const Color lightGreen = Color(0xFF81C784); // Light green for accents + static const Color paleGreen = Color(0xFF66BB6A); // Medium green + static const Color teaGreen = Color(0xFF8BC34A); // Tea leaf green + + // Background Colors + static const Color creamBackground = Color(0xFFF1F8E9); // Main cream/light green background + static const Color whiteBackground = Color(0xFFFFFFFF); + static const Color cardBackground = Color(0xFFE8F5E8); // Light green card background + static const Color overlayBackground = Color(0xFFF5F5F5); // Light overlay + + // Text Colors + static const Color primaryText = Color(0xFF1B5E20); // Dark green text + static const Color secondaryText = Color(0xFF424242); // Dark gray text + static const Color lightText = Color(0xFF757575); // Light gray text + static const Color whiteText = Color(0xFFFFFFFF); + static const Color placeholderText = Color(0xFF9E9E9E); // Placeholder text + + // Accent Colors + static const Color errorRed = Color(0xFFE53935); // Red for errors/required fields + static const Color warningOrange = Color(0xFFFF9800); + static const Color successGreen = Color(0xFF4CAF50); + static const Color yellowAccent = Color(0xFFFFEB3B); // Yellow from color palette + + + // Bottom Navigation Colors + static const Color bottomNavBackground = Color(0xFF2E7D32); + static const Color bottomNavSelected = Color(0xFFFFFFFF); + static const Color bottomNavUnselected = Color.fromARGB(179, 0, 0, 0); + + + static const LinearGradient buttonGradient = LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFF66BB6A), + Color(0xFF4CAF50), + ], + ); + + static const LinearGradient cardGradient = LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFFFFFFFF), + Color(0xFFF8F8F8), + ], + ); + + // Tea Cup Icon Colors (for branding) + static const Color teaCupPrimary = Color(0xFF8D6E63); // Brown for tea cup + static const Color teaCupSecondary = Color(0xFF4CAF50); // Green tea color + static const Color teaLeafGreen = Color(0xFF689F38); // Tea leaf green + + // Text Styles + static const TextStyle appTitle = TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: primaryText, + fontFamily: 'Inter', + letterSpacing: -0.5, + ); + + static const TextStyle welcomeTitle = TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: primaryText, + fontFamily: 'Inter', + ); + + static const TextStyle screenTitle = TextStyle( + fontSize: 24, + fontWeight: FontWeight.w600, + color: primaryText, + fontFamily: 'Inter', + ); + + static const TextStyle sectionTitle = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: primaryText, + fontFamily: 'Inter', + ); + + static const TextStyle cardTitle = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: primaryText, + fontFamily: 'Inter', + ); + + static const TextStyle bodyLarge = TextStyle( + fontSize: 16, + fontWeight: FontWeight.normal, + color: secondaryText, + fontFamily: 'Inter', + ); + + static const TextStyle bodyMedium = TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: secondaryText, + fontFamily: 'Inter', + ); + + static const TextStyle bodySmall = TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: lightText, + fontFamily: 'Inter', + ); + + static const TextStyle buttonText = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: whiteText, + fontFamily: 'Inter', + ); + + static const TextStyle linkText = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: primaryGreen, + decoration: TextDecoration.underline, + fontFamily: 'Inter', + ); + + // Dashboard specific text styles + static const TextStyle dashboardMetricNumber = TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: primaryGreen, + fontFamily: 'Inter', + ); + + static const TextStyle dashboardMetricLabel = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: secondaryText, + fontFamily: 'Inter', + ); + + static const TextStyle dashboardSubtitle = TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: lightText, + fontFamily: 'Inter', + ); + + // Form field styles + static const TextStyle fieldLabel = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: primaryText, + fontFamily: 'Inter', + ); + + static const TextStyle fieldError = TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: errorRed, + fontFamily: 'Inter', + ); + + // Input Decoration + static InputDecoration inputDecoration({ + required String labelText, + String? hintText, + IconData? prefixIcon, + Widget? suffixIcon, + bool isRequired = false, + }) { + return InputDecoration( + labelText: labelText, + hintText: hintText, + prefixIcon: prefixIcon != null ? Icon(prefixIcon, color: primaryGreen) : null, + suffixIcon: suffixIcon, + filled: true, + fillColor: whiteBackground, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: lightGreen, width: 1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: lightGreen, width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: primaryGreen, width: 2), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: errorRed, width: 1), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: errorRed, width: 2), + ), + labelStyle: fieldLabel, + hintStyle: TextStyle(color: placeholderText), + errorStyle: fieldError, + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + ); + } + + // Button Styles + static final ElevatedButtonThemeData elevatedButtonTheme = ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: primaryGreen, + foregroundColor: whiteText, + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 2, + shadowColor: primaryGreen.withOpacity(0.3), + textStyle: buttonText, + ), + ); + + + // AppBar Theme + static const AppBarTheme appBarTheme = AppBarTheme( + backgroundColor: Colors.transparent, + elevation: 0, + iconTheme: IconThemeData(color: primaryText), + titleTextStyle: screenTitle, + centerTitle: true, + ); + + // Bottom Navigation Bar Theme + static final BottomNavigationBarThemeData bottomNavTheme = BottomNavigationBarThemeData( + backgroundColor: bottomNavBackground, + selectedItemColor: bottomNavSelected, + unselectedItemColor: bottomNavUnselected, + type: BottomNavigationBarType.fixed, + elevation: 8, + selectedLabelStyle: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: 'Inter', + ), + unselectedLabelStyle: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.normal, + fontFamily: 'Inter', + ), + ); + + // Main Theme Data + static ThemeData get lightTheme { + return ThemeData( + useMaterial3: true, + + // Color Scheme + colorScheme: const ColorScheme.light( + primary: primaryGreen, + primaryContainer: lightGreen, + secondary: teaGreen, + secondaryContainer: cardBackground, + surface: whiteBackground, + error: errorRed, + onPrimary: whiteText, + onSecondary: primaryText, + onSurface: primaryText, + onError: whiteText, + ), + + // Scaffold Background + scaffoldBackgroundColor: creamBackground, + + // App Bar Theme + appBarTheme: appBarTheme, + + // Bottom Navigation Theme + bottomNavigationBarTheme: bottomNavTheme, + + // Input Decoration Theme + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: whiteBackground, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: lightGreen, width: 1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: lightGreen, width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: primaryGreen, width: 2), + ), + labelStyle: fieldLabel, + hintStyle: TextStyle(color: placeholderText), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + ), + + // Text Theme + textTheme: const TextTheme( + displayLarge: appTitle, + displayMedium: welcomeTitle, + displaySmall: screenTitle, + headlineLarge: welcomeTitle, + headlineMedium: screenTitle, + headlineSmall: sectionTitle, + titleLarge: sectionTitle, + titleMedium: cardTitle, + titleSmall: bodyLarge, + bodyLarge: bodyLarge, + bodyMedium: bodyMedium, + bodySmall: bodySmall, + labelLarge: buttonText, + labelMedium: bodyMedium, + labelSmall: bodySmall, + ), + + // Icon Theme + iconTheme: const IconThemeData( + color: primaryGreen, + size: 24, + ), + + // Divider Theme + dividerTheme: DividerThemeData( + color: lightGreen.withOpacity(0.3), + thickness: 1, + space: 16, + ), + + // Checkbox Theme + checkboxTheme: CheckboxThemeData( + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return primaryGreen; + } + return null; + }), + checkColor: WidgetStateProperty.all(whiteText), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + ), + + // Font Family + fontFamily: 'Inter', + ); + } + + static BoxDecoration get dashboardCardDecoration => BoxDecoration( + color: whiteBackground, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: primaryGreen.withOpacity(0.08), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ); + + static BoxDecoration get factoryCardDecoration => BoxDecoration( + color: whiteBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: lightGreen.withOpacity(0.3), width: 1), + ); + + // FAB decoration for the centered add button + static BoxDecoration get fabDecoration => BoxDecoration( + gradient: buttonGradient, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: primaryGreen.withOpacity(0.3), + blurRadius: 12, + offset: const Offset(0, 6), + ), + ], + ); + + // Welcome screen specific decorations + static BoxDecoration get welcomeBackgroundDecoration => const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFF1F8E9), + Color(0xFFE8F5E8), + ], + ), + ); + + // Chart colors for analytics + static List get chartColors => [ + primaryGreen, + lightGreen, + teaGreen, + paleGreen, + darkGreen, + ]; + +} \ No newline at end of file diff --git a/lib/feature/drawer/app_drawer.dart b/lib/feature/drawer/app_drawer.dart index ad55eaa..7007d64 100644 --- a/lib/feature/drawer/app_drawer.dart +++ b/lib/feature/drawer/app_drawer.dart @@ -1,5 +1,7 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:harvest_manager/core/theme/theme.dart'; class AppDrawer extends StatelessWidget { final Function(int) onItemTapped; @@ -12,18 +14,23 @@ class AppDrawer extends StatelessWidget { @override Widget build(BuildContext context) { return Drawer( - child: ListView( - padding: EdgeInsets.zero, + backgroundColor: TeaTrackerTheme.creamBackground, + child: Column( children: [ - const DrawerHeader( - decoration: BoxDecoration( - color: Color.fromARGB(214, 13, 110, 17), + Container( + height: 80, + width: double.infinity, + decoration: const BoxDecoration( + color: TeaTrackerTheme.darkGreen, ), - child: Text( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: const Text( 'Menu', style: TextStyle( - color: Colors.white, - fontSize: 24, + color: TeaTrackerTheme.whiteBackground, + fontSize: 20, + fontWeight: FontWeight.w500, ), ), ), @@ -40,9 +47,38 @@ class AppDrawer extends StatelessWidget { title: const Text('About'), onTap: () { Navigator.pop(context); - }, ), + + const Spacer(), + const Divider(height: 1), + Container( + padding: const EdgeInsets.only(bottom: 16), + child: ListTile( + leading: const Icon(CupertinoIcons.square_arrow_right), + title: const Text('Logout'), + dense: true, + onTap: ()async { + Navigator .pop(context); + try { + await FirebaseAuth.instance.signOut(); + + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Logged out successfully')), + ); + } + } + catch (e) { + if (context.mounted){ + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error logging out: $e')), + ); + } + } + }, + ), + ), ], ), ); diff --git a/lib/feature/home/components/home_screen.dart b/lib/feature/home/components/home_screen.dart deleted file mode 100644 index eca400f..0000000 --- a/lib/feature/home/components/home_screen.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class HomeScreen extends StatefulWidget { - const HomeScreen({super.key}); - - @override - State createState() => _HomeScreenState(); -} - -class _HomeScreenState extends State { - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} \ No newline at end of file diff --git a/lib/feature/home/screens/home_screen.dart b/lib/feature/home/screens/home_screen.dart new file mode 100644 index 0000000..df75708 --- /dev/null +++ b/lib/feature/home/screens/home_screen.dart @@ -0,0 +1,19 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; + +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text("Home")), + body: ElevatedButton( + onPressed: () async { + await FirebaseAuth.instance.signOut(); + }, + child: const Text('Logout'), + ), + ); + } +} diff --git a/lib/feature/navbar/bottom_navbar.dart b/lib/feature/navbar/bottom_navbar.dart index 138cac7..46a301e 100644 --- a/lib/feature/navbar/bottom_navbar.dart +++ b/lib/feature/navbar/bottom_navbar.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:harvest_manager/core/theme/theme.dart'; import 'package:harvest_manager/feature/drawer/app_drawer.dart'; +import '../home/screens/home_screen.dart'; + class BottomNavbar extends StatefulWidget { const BottomNavbar({super.key}); @@ -13,11 +16,11 @@ class _BottomNavbarState extends State { int _selectedIndex = 0; static final List _screens = [ - Container(child: const Center(child: Text('Home Screen'))), // index 0 - Container(child: const Center(child: Text('Analytics Screen'))), // index 1 - Container(child: const Center(child: Text('Add'))), // index 2 - Container(child: const Center(child: Text('Notifications Screen'))), // index 3 - Container(child: const Center(child: Text('Menu Screen'))), // index 4 + const HomeScreen(), // index 0 - Your home screen + //const AnalyticsPage(), // index 1 - Analytics page + //const AddPage(), // index 2 - Add page + //const NotificationsPage(), // index 3 - Notifications page + const Center(child: Text('Menu Screen')), // index 4 - Keep as is since menu is in drawer ]; void _onItemTapped(int index) { @@ -29,37 +32,35 @@ class _BottomNavbarState extends State { @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: TeaTrackerTheme.creamBackground, body: _screens[_selectedIndex], drawer: AppDrawer(onItemTapped: _onItemTapped), // Use the separate drawer bottomNavigationBar: BottomAppBar( - color: const Color.fromARGB(214, 13, 110, 17), + color: TeaTrackerTheme.bottomNavBackground, + elevation: 8, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ buildNavBarItem(CupertinoIcons.home, 'Home', 0), - buildNavBarItem(CupertinoIcons.chart_bar, 'Analytics', 1), + buildNavBarItem(CupertinoIcons.chart_bar_alt_fill, 'Analytics', 1), const SizedBox( width: 20, ), buildNavBarItem(CupertinoIcons.bell, 'Notifications', 3), - buildDrawerItem(), // Drawer icon instead of menu + buildDrawerItem(), ], ), ), - floatingActionButton: ClipOval( - child: Material( - color: const Color.fromARGB(214, 155, 236, 157), - elevation: 10, - child: InkWell( - child: const SizedBox( - width: 56, - height: 56, - child: Icon( - CupertinoIcons.add_circled, - size: 56, - color: Colors.black, - ), - ), + floatingActionButton: Container( + decoration: TeaTrackerTheme.fabDecoration, + child: FloatingActionButton( + onPressed: () => _onItemTapped(2), + backgroundColor: Colors.transparent, + elevation: 0, + child: const Icon( + CupertinoIcons.add, + size: 28, + color: TeaTrackerTheme.whiteText, ), ), ), @@ -70,21 +71,23 @@ class _BottomNavbarState extends State { Widget buildNavBarItem(IconData icon, String label, int index) { return InkWell( onTap: () => _onItemTapped(index), + borderRadius: BorderRadius.circular(8), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, color: _selectedIndex == index - ? const Color.fromARGB(214, 155, 236, 157) - : Colors.black87, + ? TeaTrackerTheme.bottomNavSelected + : TeaTrackerTheme.bottomNavUnselected, ), Text( label, style: TextStyle( color: _selectedIndex == index - ? const Color.fromARGB(214, 155, 236, 157) - : Colors.black87, + ? TeaTrackerTheme.bottomNavSelected + : TeaTrackerTheme.bottomNavUnselected, + fontFamily: 'Inder', ), ), ], @@ -99,17 +102,18 @@ class _BottomNavbarState extends State { onTap: () { Scaffold.of(context).openDrawer(); }, + borderRadius: BorderRadius.circular(8), child: const Column( mainAxisSize: MainAxisSize.min, children: [ Icon( CupertinoIcons.line_horizontal_3, - color: Colors.black87, + color: TeaTrackerTheme.bottomNavUnselected, ), Text( 'Menu', style: TextStyle( - color: Colors.black87, + color: TeaTrackerTheme.bottomNavUnselected, ), ), ], diff --git a/lib/main.dart b/lib/main.dart index 3b3d537..f244c19 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,10 +15,11 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + return MaterialApp( - title: 'Flutter Demo', + title: 'Harvest Manager', debugShowCheckedModeBanner: false, - home: Wrapper(), + home: const Wrapper(), ); } } \ No newline at end of file diff --git a/lib/wrapper.dart b/lib/wrapper.dart index a49da07..4676e23 100644 --- a/lib/wrapper.dart +++ b/lib/wrapper.dart @@ -1,6 +1,6 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; -import 'package:harvest_manager/feature/home/screens/home_screen.dart'; +import 'package:harvest_manager/feature/navbar/bottom_navbar.dart'; import 'package:harvest_manager/feature/sign-up/screens/sign_up_screen.dart'; import 'package:harvest_manager/feature/splash-screen/screens/splash_screen.dart'; import 'package:harvest_manager/feature/sign-in/screens/sign_in_screen.dart'; @@ -48,7 +48,7 @@ class _WrapperState extends State { if (!_isDelayOver || snapshot.connectionState == ConnectionState.waiting) { return const SplashScreen(); } else if (snapshot.hasData) { - return const HomeScreen(); + return const BottomNavbar(); } else { return switchPages ? SignInFormScreen(onToggle: toggleScreens)