commit 545e092d3b1952ca18eedf010212e279c7bfdff1 Author: LemonNexus Date: Sat Feb 14 03:24:28 2026 +0000 feat: LifeFlow - Personal Life Management System - Initial Commit PROJECT FOUNDATION: - Complete project structure (Android, PC, Backend, HA) - Comprehensive README with vision and features - Detailed development plan (6 week timeline) ANDROID APP - MVP STRUCTURE: - pubspec.yaml with all dependencies (Riverpod, Hive, Flutter) - Data models with Hive annotations: - Routine (with categories, schedules, reminders) - Activity (completion tracking, mood) - Gamification (stats, badges, levels) - Repository layer with Riverpod providers - Beautiful dashboard UI with: - Daily progress ring - Today's routines list - Quick stats (hydration, meds, points) - Streak visualization - Add routine bottom sheet - Complete theme system (Light/Dark) FEATURES PLANNED: - 10 routine categories (meds, vitamins, appointments, sleep, food, hydration, exercise, hygiene, self-care) - Gamification (points, streaks, badges, levels) - SMS parsing for appointments - Home Assistant integration - PC companion app - Cloud sync Status: Ready for development! diff --git a/README.md b/README.md new file mode 100644 index 0000000..a37476a --- /dev/null +++ b/README.md @@ -0,0 +1,252 @@ +# LifeFlow - Personal Life Management System + +> Transform your daily routines into enjoyable habits +> +> **Android App** | **PC App** | **Home Assistant Integration** | **SMS Parsing** + +--- + +## ๐ŸŽฏ Vision + +LifeFlow makes managing your daily life **effortless, engaging, and rewarding**. From medications to hydration, appointments to self-care - everything organized in one beautiful, intuitive app. + +### Core Principles +- **Easy** - Minimal friction to log activities +- **Fun** - Gamification and rewards +- **Smart** - AI-powered suggestions and SMS parsing +- **Connected** - Syncs across all devices + Home Assistant + +--- + +## ๐Ÿ“ฑ Features Overview + +### 1. Medication & Vitamins ๐Ÿ’Š +- **Smart Reminders** - Time-based with snooze options +- **Dose Tracking** - Log what you took and when +- **Refill Alerts** - Warns when running low +- **Interaction Checker** - Flags potential conflicts +- **History View** - Complete medication timeline + +### 2. Appointments & Meetings ๐Ÿ“… +- **Calendar Sync** - Google/Outlook integration +- **SMS Parsing** - Auto-extract from text messages +- **Smart Scheduling** - Suggests optimal times +- **Prep Reminders** - Reminds you to prepare +- **Travel Time** - Includes commute in notifications + +### 3. Sleep Schedule ๐Ÿ˜ด +- **Sleep Tracking** - Duration and quality +- **Wind-Down Alerts** - Prepare for bedtime +- **Smart Alarm** - Wake at optimal cycle +- **Sleep Hygiene** - Tips and reminders +- **Trends Analysis** - Weekly/monthly patterns + +### 4. Food & Intake ๐ŸŽ +- **Meal Logging** - Quick photo + notes +- **Macro Tracking** - Protein, carbs, fats +- **Water Intake** - Hydration reminders +- **Calorie Estimation** - Smart suggestions +- **Meal Prep Reminders** - Plan ahead + +### 5. Self Care & Hygiene ๐Ÿง˜ +- **Routine Checklists** - Morning/evening rituals +- **Skincare Tracking** - Product usage log +- **Exercise Logging** - Workout tracking +- **Mental Health** - Mood journaling +- **Meditation Timer** - Mindfulness sessions + +### 6. Hydration ๐Ÿ’ง +- **Smart Reminders** - Based on activity level +- **Quick Log** - One-tap water logging +- **Goal Tracking** - Daily target visualization +- **Streak Counter** - Consecutive days hit +- **Weather Aware** - More reminders on hot days + +--- + +## ๐ŸŽฎ Gamification System + +### Points & Rewards +- **Activity Points** - Earn for completing routines +- **Streak Bonuses** - Multipliers for consistency +- **Achievement Badges** - Unlock milestones +- **Level System** - Progress from Novice to Master +- **Weekly Challenges** - Special themed goals + +### Visual Progress +- **Flame Streaks** - Visual fire for active streaks +- **Progress Rings** - Daily completion circles +- **Monthly Calendar** - Heat map of activity +- **Trend Graphs** - Visualize improvements + +--- + +## ๐Ÿ  Home Assistant Integration + +### Features +- **Dashboard Widget** - Quick status view +- **Voice Commands** - "Hey Google, log my vitamins" +- **Automations** - Trigger based on routines + - Lights dim at bedtime + - Coffee maker starts after morning routine + - Notifications to family members +- **Sensors** - Track completion rates +- **Scripts** - Run routines from HA + +### Entities Exposed +- `sensor.lifeflow_medication_today` +- `sensor.lifeflow_water_intake` +- `sensor.lifeflow_sleep_quality` +- `binary_sensor.lifeflow_all_routines_done` +- `button.lifeflow_log_vitamins` + +--- + +## ๐Ÿ’ป PC App Features + +### Desktop Companion +- **Quick Log Widget** - Desktop overlay +- **Keyboard Shortcuts** - Ctrl+Shift+L for water +- **System Tray** - Always accessible +- **Calendar View** - Weekly planning +- **Data Export** - CSV/JSON for analysis + +### Cross-Platform Sync +- **Real-time Sync** - Instant across devices +- **Offline Mode** - Works without internet +- **Conflict Resolution** - Smart merge +- **Backup & Restore** - Never lose data + +--- + +## ๐Ÿ“ฑ Android App Architecture + +### Tech Stack +- **Framework:** Flutter (cross-platform) +- **State Management:** Riverpod +- **Local DB:** Hive (fast, lightweight) +- **Backend:** Firebase / Supabase +- **Notifications:** Flutter Local Notifications +- **SMS:** Telephony plugin + +### Screens +1. **Dashboard** - Today's overview +2. **Routines** - All routine categories +3. **Calendar** - Schedule view +4. **Stats** - Progress & analytics +5. **Settings** - Configuration +6. **Add Routine** - Create new routines + +--- + +## ๐Ÿค– SMS Parsing AI + +### Extracts From Texts +**Appointment SMS:** +``` +"Your appointment with Dr. Smith is +confirmed for March 15 at 2:30 PM at +Downtown Medical Center" +``` +โ†’ Auto-adds to calendar with location + +**Medication Reminders:** +``` +"Your prescription is ready for pickup +at CVS Pharmacy. Refill #3 of 5." +``` +โ†’ Logs refill, updates inventory + +**Meeting Invites:** +``` +"Team standup tomorrow 9 AM in Conference +Room B. Bring your reports." +``` +โ†’ Adds meeting with notes + +--- + +## ๐Ÿ“Š Data Model + +### Core Entities +``` +User +โ”œโ”€โ”€ Routines[] +โ”œโ”€โ”€ Activities[] +โ”œโ”€โ”€ Medications[] +โ”œโ”€โ”€ Appointments[] +โ”œโ”€โ”€ SleepLogs[] +โ”œโ”€โ”€ FoodLogs[] +โ””โ”€โ”€ Settings + +Routine +โ”œโ”€โ”€ name: string +โ”œโ”€โ”€ category: enum +โ”œโ”€โ”€ schedule: CronExpression +โ”œโ”€โ”€ reminders: Reminder[] +โ”œโ”€โ”€ points: number +โ””โ”€โ”€ streak: number + +Activity +โ”œโ”€โ”€ routineId: string +โ”œโ”€โ”€ timestamp: datetime +โ”œโ”€โ”€ completed: boolean +โ”œโ”€โ”€ notes: string +โ””โ”€โ”€ mood: enum +``` + +--- + +## ๐Ÿ” Privacy & Security + +- **Local-First** - Data stored on device +- **Encrypted Sync** - End-to-end encryption +- **No Data Selling** - Your data is yours +- **Export Anytime** - Full data portability +- **Optional Cloud** - Sync only if you want + +--- + +## ๐Ÿš€ Development Phases + +### Phase 1: Core Android App +- Basic routine tracking +- Notifications +- Local storage +- Simple gamification + +### Phase 2: Smart Features +- SMS parsing +- AI suggestions +- Advanced analytics +- Widgets + +### Phase 3: Ecosystem +- PC app +- Home Assistant +- Cloud sync +- Social features + +--- + +## ๐Ÿ“ˆ Success Metrics + +- Daily active users +- Routine completion rates +- User retention (7/30/90 day) +- Average streak length +- Feature adoption + +--- + +## ๐ŸŽจ Design Philosophy + +- **Calming Colors** - Blues, greens, soft tones +- **Minimalist** - Clean, uncluttered UI +- **Accessible** - WCAG compliant +- **Delightful** - Micro-interactions, animations +- **Personal** - Customizable themes + +--- + +Ready to build! ๐Ÿš€ diff --git a/android/lib/main.dart b/android/lib/main.dart new file mode 100644 index 0000000..18ab308 --- /dev/null +++ b/android/lib/main.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'screens/dashboard/dashboard_screen.dart'; +import 'screens/routines/routines_screen.dart'; +import 'screens/calendar/calendar_screen.dart'; +import 'screens/stats/stats_screen.dart'; +import 'screens/settings/settings_screen.dart'; +import 'theme/app_theme.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // Initialize Hive (local database) + // await Hive.initFlutter(); + // _registerAdapters(); + + runApp( + const ProviderScope( + child: LifeFlowApp(), + ), + ); +} + +class LifeFlowApp extends StatelessWidget { + const LifeFlowApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'LifeFlow', + debugShowCheckedModeBanner: false, + theme: AppTheme.lightTheme, + darkTheme: AppTheme.darkTheme, + themeMode: ThemeMode.system, + home: const MainNavigationScreen(), + ); + } +} + +class MainNavigationScreen extends StatefulWidget { + const MainNavigationScreen({super.key}); + + @override + State createState() => _MainNavigationScreenState(); +} + +class _MainNavigationScreenState extends State { + int _currentIndex = 0; + + final _screens = const [ + DashboardScreen(), + RoutinesScreen(), + CalendarScreen(), + StatsScreen(), + SettingsScreen(), + ]; + + final _destinations = const [ + NavigationDestination( + icon: Icon(Icons.dashboard_outlined), + selectedIcon: Icon(Icons.dashboard), + label: 'Home', + ), + NavigationDestination( + icon: Icon(Icons.checklist_outlined), + selectedIcon: Icon(Icons.checklist), + label: 'Routines', + ), + NavigationDestination( + icon: Icon(Icons.calendar_today_outlined), + selectedIcon: Icon(Icons.calendar_today), + label: 'Calendar', + ), + NavigationDestination( + icon: Icon(Icons.insights_outlined), + selectedIcon: Icon(Icons.insights), + label: 'Stats', + ), + NavigationDestination( + icon: Icon(Icons.settings_outlined), + selectedIcon: Icon(Icons.settings), + label: 'Settings', + ), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: _screens[_currentIndex], + ), + bottomNavigationBar: NavigationBar( + selectedIndex: _currentIndex, + onDestinationSelected: (index) { + setState(() { + _currentIndex = index; + }); + }, + destinations: _destinations, + ), + ); + } +} diff --git a/android/lib/models/activity.dart b/android/lib/models/activity.dart new file mode 100644 index 0000000..dfae6bc --- /dev/null +++ b/android/lib/models/activity.dart @@ -0,0 +1,175 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'routine.dart'; + +part 'activity.freezed.dart'; +part 'activity.g.dart'; + +@HiveType(typeId: 5) +enum Mood { + @HiveField(0) + terrible, + @HiveField(1) + bad, + @HiveField(2) + neutral, + @HiveField(3) + good, + @HiveField(4) + great, +} + +@HiveType(typeId: 6) +@freezed +class Activity with _$Activity { + const factory Activity({ + @HiveField(0) required String id, + @HiveField(1) required String routineId, + @HiveField(2) required DateTime timestamp, + @HiveField(3) @Default(true) bool completed, + @HiveField(4) String? notes, + @HiveField(5) Mood? mood, + @HiveField(6) @Default(0) int pointsEarned, + @HiveField(7) DateTime? scheduledTime, + }) = _Activity; + + factory Activity.fromJson(Map json) = + _$$ActivityImplFromJson; +} + +extension MoodExtension on Mood { + String get displayName { + switch (this) { + case Mood.terrible: + return 'Terrible'; + case Mood.bad: + return 'Bad'; + case Mood.neutral: + return 'Okay'; + case Mood.good: + return 'Good'; + case Mood.great: + return 'Great!'; + } + } + + String get emoji { + switch (this) { + case Mood.terrible: + return '๐Ÿ˜ซ'; + case Mood.bad: + return '๐Ÿ˜•'; + case Mood.neutral: + return '๐Ÿ˜'; + case Mood.good: + return '๐Ÿ™‚'; + case Mood.great: + return '๐Ÿ˜„'; + } + } +} + +@HiveType(typeId: 7) +@freezed +class ActivityStats with _$ActivityStats { + const factory ActivityStats({ + @HiveField(0) required int totalCompleted, + @HiveField(1) required int totalSkipped, + @HiveField(2) required double completionRate, + @HiveField(3) required int currentStreak, + @HiveField(4) required int longestStreak, + @HiveField(5) required int totalPoints, + @HiveField(6) @Default({}) Map categoryCompletion, + }) = _ActivityStats; + + factory ActivityStats.fromJson(Map json) = + _$$ActivityStatsImplFromJson; +} + +// Helper class for daily progress +class DailyProgress { + final DateTime date; + final int totalRoutines; + final int completedRoutines; + final int totalPoints; + final bool allCompleted; + + DailyProgress({ + required this.date, + required this.totalRoutines, + required this.completedRoutines, + required this.totalPoints, + }) : allCompleted = totalRoutines > 0 && totalRoutines == completedRoutines; + + double get completionPercentage = + totalRoutines > 0 ? (completedRoutines / totalRoutines) * 100 : 0; +} + +// Helper for streak calculation +class StreakCalculator { + static int calculateCurrentStreak(List activities) { + if (activities.isEmpty) return 0; + + // Sort by date descending + final sorted = activities.toList() + ..sort((a, b) => b.timestamp.compareTo(a.timestamp)); + + int streak = 0; + DateTime checkDate = DateTime.now(); + + for (final activity in sorted) { + final activityDate = DateTime( + activity.timestamp.year, + activity.timestamp.month, + activity.timestamp.day, + ); + + final checkDateOnly = DateTime( + checkDate.year, + checkDate.month, + checkDate.day, + ); + + if (activityDate.isAtSameMomentAs(checkDateOnly) && activity.completed) { + streak++; + checkDate = checkDate.subtract(const Duration(days: 1)); + } else if (activityDate.isBefore(checkDateOnly)) { + break; + } + } + + return streak; + } + + static int calculateLongestStreak(List activities) { + if (activities.isEmpty) return 0; + + // Group by day + final dailyCompletion = {}; + for (final activity in activities) { + final date = DateTime( + activity.timestamp.year, + activity.timestamp.month, + activity.timestamp.day, + ); + dailyCompletion[date] = (dailyCompletion[date] ?? true) && activity.completed; + } + + // Sort dates + final dates = dailyCompletion.keys.toList()..sort(); + + int longestStreak = 0; + int currentStreak = 0; + + for (int i = 0; i < dates.length; i++) { + if (dailyCompletion[dates[i]] == true) { + currentStreak++; + longestStreak = longestStreak > currentStreak ? longestStreak : currentStreak; + } else { + currentStreak = 0; + } + } + + return longestStreak; + } +} diff --git a/android/lib/models/gamification.dart b/android/lib/models/gamification.dart new file mode 100644 index 0000000..8e84e71 --- /dev/null +++ b/android/lib/models/gamification.dart @@ -0,0 +1,275 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; + +part 'gamification.freezed.dart'; +part 'gamification.g.dart'; + +@HiveType(typeId: 8) +@freezed +class GamificationStats with _$GamificationStats { + const factory GamificationStats({ + @HiveField(0) @Default(0) int totalPoints, + @HiveField(1) @Default(0) int currentStreak, + @HiveField(2) @Default(0) int longestStreak, + @HiveField(3) @Default(1) int level, + @HiveField(4) @Default([]) List unlockedBadges, + @HiveField(5) @Default({}) Map categoryStreaks, + @HiveField(6) @Default(0) int routinesCompletedToday, + @HiveField(7) @Default(0) int totalRoutinesCompleted, + @HiveField(8) @Default(0) int perfectDays, + @HiveField(9) required DateTime lastUpdated, + }) = _GamificationStats; + + factory GamificationStats.fromJson(Map json) = + _$$GamificationStatsImplFromJson; +} + +@HiveType(typeId: 9) +class Badge extends HiveObject { + @HiveField(0) + final String id; + + @HiveField(1) + final String name; + + @HiveField(2) + final String description; + + @HiveField(3) + final String icon; + + @HiveField(4) + final int pointsRequired; + + @HiveField(5) + final BadgeCategory category; + + Badge({ + required this.id, + required this.name, + required this.description, + required this.icon, + required this.pointsRequired, + required this.category, + }); +} + +@HiveType(typeId: 10) +enum BadgeCategory { + @HiveField(0) + streak, + @HiveField(1) + completion, + @HiveField(2) + category, + @HiveField(3) + special, +} + +// Predefined badges +class Badges { + static final List all = [ + // Streak badges + Badge( + id: 'streak_3', + name: 'Getting Started', + description: '3 day streak', + icon: '๐Ÿ”ฅ', + pointsRequired: 0, + category: BadgeCategory.streak, + ), + Badge( + id: 'streak_7', + name: 'Week Warrior', + description: '7 day streak', + icon: '๐Ÿ”ฅ๐Ÿ”ฅ', + pointsRequired: 0, + category: BadgeCategory.streak, + ), + Badge( + id: 'streak_30', + name: 'Monthly Master', + description: '30 day streak', + icon: '๐Ÿ“…', + pointsRequired: 0, + category: BadgeCategory.streak, + ), + Badge( + id: 'streak_100', + name: 'Centurion', + description: '100 day streak', + icon: '๐Ÿ’ฏ', + pointsRequired: 0, + category: BadgeCategory.streak, + ), + + // Completion badges + Badge( + id: 'first_routine', + name: 'First Step', + description: 'Complete your first routine', + icon: '๐Ÿ‘Ÿ', + pointsRequired: 0, + category: BadgeCategory.completion, + ), + Badge( + id: 'routines_10', + name: 'Getting Consistent', + description: 'Complete 10 routines', + icon: 'โญ', + pointsRequired: 0, + category: BadgeCategory.completion, + ), + Badge( + id: 'routines_100', + name: 'Century Club', + description: 'Complete 100 routines', + icon: '๐Ÿ†', + pointsRequired: 0, + category: BadgeCategory.completion, + ), + Badge( + id: 'perfect_week', + name: 'Perfect Week', + description: 'Complete all routines for 7 days', + icon: '๐ŸŒŸ', + pointsRequired: 0, + category: BadgeCategory.completion, + ), + + // Category badges + Badge( + id: 'hydration_master', + name: 'Hydration Hero', + description: 'Hit water goal 7 days in a row', + icon: '๐Ÿ’ง', + pointsRequired: 0, + category: BadgeCategory.category, + ), + Badge( + id: 'medication_reliable', + name: 'Reliable', + description: 'Take medication on time for 14 days', + icon: '๐Ÿ’Š', + pointsRequired: 0, + category: BadgeCategory.category, + ), + Badge( + id: 'sleep_schedule', + name: 'Sleep Master', + description: 'Maintain sleep schedule for 7 days', + icon: '๐Ÿ˜ด', + pointsRequired: 0, + category: BadgeCategory.category, + ), + + // Special badges + Badge( + id: 'early_bird', + name: 'Early Bird', + description: 'Complete morning routine before 8 AM', + icon: '๐Ÿฆ', + pointsRequired: 0, + category: BadgeCategory.special, + ), + Badge( + id: 'night_owl', + name: 'Night Owl', + description: 'Complete evening routine after 9 PM', + icon: '๐Ÿฆ‰', + pointsRequired: 0, + category: BadgeCategory.special, + ), + ]; +} + +// Level calculation +class LevelCalculator { + static int calculateLevel(int totalPoints) { + // Exponential level curve + // Level 1: 0 points + // Level 2: 100 points + // Level 3: 250 points + // Level 4: 450 points + // etc. + + if (totalPoints < 100) return 1; + if (totalPoints < 250) return 2; + if (totalPoints < 450) return 3; + if (totalPoints < 700) return 4; + if (totalPoints < 1000) return 5; + + // Beyond level 5: every 500 points + return 5 + ((totalPoints - 1000) ~/ 500); + } + + static int pointsToNextLevel(int currentLevel, int totalPoints) { + final nextLevelThreshold = _levelThreshold(currentLevel + 1); + return nextLevelThreshold - totalPoints; + } + + static double progressToNextLevel(int currentLevel, int totalPoints) { + final currentThreshold = _levelThreshold(currentLevel); + final nextThreshold = _levelThreshold(currentLevel + 1); + final progress = totalPoints - currentThreshold; + final needed = nextThreshold - currentThreshold; + return (progress / needed).clamp(0.0, 1.0); + } + + static int _levelThreshold(int level) { + switch (level) { + case 1: + return 0; + case 2: + return 100; + case 3: + return 250; + case 4: + return 450; + case 5: + return 700; + default: + return 1000 + ((level - 6) * 500); + } + } +} + +class BadgeChecker { + static List checkUnlockedBadges( + GamificationStats stats, + List alreadyUnlocked, + ) { + final newlyUnlocked = []; + + for (final badge in Badges.all) { + if (alreadyUnlocked.contains(badge.id)) continue; + + if (_meetsCriteria(badge, stats)) { + newlyUnlocked.add(badge); + } + } + + return newlyUnlocked; + } + + static bool _meetsCriteria(Badge badge, GamificationStats stats) { + switch (badge.id) { + case 'streak_3': + return stats.currentStreak >= 3; + case 'streak_7': + return stats.currentStreak >= 7; + case 'streak_30': + return stats.currentStreak >= 30; + case 'streak_100': + return stats.currentStreak >= 100; + case 'routines_10': + return stats.totalRoutinesCompleted >= 10; + case 'routines_100': + return stats.totalRoutinesCompleted >= 100; + case 'perfect_week': + return stats.perfectDays >= 7; + default: + return false; + } + } +} diff --git a/android/lib/models/routine.dart b/android/lib/models/routine.dart new file mode 100644 index 0000000..ece93b4 --- /dev/null +++ b/android/lib/models/routine.dart @@ -0,0 +1,162 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; + +part 'routine.freezed.dart'; +part 'routine.g.dart'; + +@HiveType(typeId: 0) +enum RoutineCategory { + @HiveField(0) + medication, + @HiveField(1) + vitamin, + @HiveField(2) + appointment, + @HiveField(3) + sleep, + @HiveField(4) + food, + @HiveField(5) + hydration, + @HiveField(6) + exercise, + @HiveField(7) + hygiene, + @HiveField(8) + selfCare, + @HiveField(9) + custom, +} + +@HiveType(typeId: 1) +@freezed +class Routine with _$Routine { + const factory Routine({ + @HiveField(0) required String id, + @HiveField(1) required String name, + @HiveField(2) String? description, + @HiveField(3) required RoutineCategory category, + @HiveField(4) required Schedule schedule, + @HiveField(5) @Default([]) List reminders, + @HiveField(6) @Default(10) int points, + @HiveField(7) String? icon, + @HiveField(8) String? color, + @HiveField(9) @Default(true) bool isActive, + @HiveField(10) required DateTime createdAt, + @HiveField(11) DateTime? updatedAt, + }) = _Routine; + + factory Routine.fromJson(Map json) = + _$$RoutineImplFromJson; +} + +@HiveType(typeId: 2) +@freezed +class Schedule with _$Schedule { + const factory Schedule({ + @HiveField(0) required ScheduleType type, + @HiveField(1) List? daysOfWeek, // 0 = Monday, 6 = Sunday + @HiveField(2) DateTime? specificDate, + @HiveField(3) required String time, // "HH:mm" format + @HiveField(4) int? intervalDays, // For every X days + }) = _Schedule; + + factory Schedule.fromJson(Map json) = + _$$ScheduleImplFromJson; +} + +@HiveType(typeId: 3) +enum ScheduleType { + @HiveField(0) + daily, + @HiveField(1) + weekly, + @HiveField(2) + specificDate, + @HiveField(3) + interval, +} + +@HiveType(typeId: 4) +@freezed +class Reminder with _$Reminder { + const factory Reminder({ + @HiveField(0) required String id, + @HiveField(1) required int minutesBefore, + @HiveField(2) @Default(true) bool isEnabled, + }) = _Reminder; + + factory Reminder.fromJson(Map json) = + _$$ReminderImplFromJson; +} + +// Extension for category metadata +extension RoutineCategoryExtension on RoutineCategory { + String get displayName { + switch (this) { + case RoutineCategory.medication: + return 'Medication'; + case RoutineCategory.vitamin: + return 'Vitamins'; + case RoutineCategory.appointment: + return 'Appointments'; + case RoutineCategory.sleep: + return 'Sleep'; + case RoutineCategory.food: + return 'Food & Nutrition'; + case RoutineCategory.hydration: + return 'Hydration'; + case RoutineCategory.exercise: + return 'Exercise'; + case RoutineCategory.hygiene: + return 'Hygiene'; + case RoutineCategory.selfCare: + return 'Self Care'; + case RoutineCategory.custom: + return 'Custom'; + } + } + + String get icon { + switch (this) { + case RoutineCategory.medication: + return '๐Ÿ’Š'; + case RoutineCategory.vitamin: + return '๐ŸŠ'; + case RoutineCategory.appointment: + return '๐Ÿ“…'; + case RoutineCategory.sleep: + return '๐Ÿ˜ด'; + case RoutineCategory.food: + return '๐ŸŽ'; + case RoutineCategory.hydration: + return '๐Ÿ’ง'; + case RoutineCategory.exercise: + return '๐Ÿƒ'; + case RoutineCategory.hygiene: + return '๐Ÿงผ'; + case RoutineCategory.selfCare: + return '๐Ÿง˜'; + case RoutineCategory.custom: + return 'โœจ'; + } + } + + int get defaultPoints { + switch (this) { + case RoutineCategory.medication: + case RoutineCategory.vitamin: + return 20; + case RoutineCategory.appointment: + return 15; + case RoutineCategory.exercise: + return 25; + case RoutineCategory.sleep: + return 30; + case RoutineCategory.hydration: + return 10; + default: + return 15; + } + } +} diff --git a/android/lib/repositories/routine_repository.dart b/android/lib/repositories/routine_repository.dart new file mode 100644 index 0000000..61204c7 --- /dev/null +++ b/android/lib/repositories/routine_repository.dart @@ -0,0 +1,120 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hive/hive.dart'; +import '../models/routine.dart'; + +final routineRepositoryProvider = Provider((ref) { + return RoutineRepository(); +}); + +final routinesProvider = StreamProvider>((ref) { + final repository = ref.watch(routineRepositoryProvider); + return repository.watchAllRoutines(); +}); + +final todayRoutinesProvider = FutureProvider>((ref) async { + final repository = ref.watch(routineRepositoryProvider); + return repository.getTodaysRoutines(); +}); + +class RoutineRepository { + static const String _boxName = 'routines'; + Box? _box; + + Future> _getBox() async { + _box ??= await Hive.openBox(_boxName); + return _box!; + } + + // Create + Future createRoutine(Routine routine) async { + final box = await _getBox(); + await box.put(routine.id, routine); + } + + // Read + Future getRoutine(String id) async { + final box = await _getBox(); + return box.get(id); + } + + Future> getAllRoutines() async { + final box = await _getBox(); + return box.values.toList(); + } + + Stream> watchAllRoutines() async* { + final box = await _getBox(); + yield box.values.toList(); + yield* box.watch().map((_) => box.values.toList()); + } + + Future> getRoutinesByCategory(RoutineCategory category) async { + final routines = await getAllRoutines(); + return routines.where((r) => r.category == category).toList(); + } + + Future> getTodaysRoutines() async { + final routines = await getAllRoutines(); + final now = DateTime.now(); + final weekday = now.weekday - 1; // 0 = Monday, 6 = Sunday + + return routines.where((routine) { + if (!routine.isActive) return false; + + switch (routine.schedule.type) { + case ScheduleType.daily: + return true; + case ScheduleType.weekly: + return routine.schedule.daysOfWeek?.contains(weekday) ?? false; + case ScheduleType.specificDate: + final specificDate = routine.schedule.specificDate; + if (specificDate == null) return false; + return specificDate.year == now.year && + specificDate.month == now.month && + specificDate.day == now.day; + case ScheduleType.interval: + // Simplified: check if today falls on interval + final createdDate = DateTime( + routine.createdAt.year, + routine.createdAt.month, + routine.createdAt.day, + ); + final today = DateTime(now.year, now.month, now.day); + final daysDiff = today.difference(createdDate).inDays; + final interval = routine.schedule.intervalDays ?? 1; + return daysDiff % interval == 0; + } + }).toList(); + } + + // Update + Future updateRoutine(Routine routine) async { + final box = await _getBox(); + await box.put(routine.id, routine.copyWith(updatedAt: DateTime.now())); + } + + Future toggleRoutineActive(String id) async { + final routine = await getRoutine(id); + if (routine != null) { + await updateRoutine(routine.copyWith(isActive: !routine.isActive)); + } + } + + // Delete + Future deleteRoutine(String id) async { + final box = await _getBox(); + await box.delete(id); + } + + // Statistics + Future> getRoutineCountsByCategory() async { + final routines = await getAllRoutines(); + final counts = {}; + + for (final routine in routines) { + counts[routine.category] = (counts[routine.category] ?? 0) + 1; + } + + return counts; + } +} diff --git a/android/lib/screens/dashboard/dashboard_screen.dart b/android/lib/screens/dashboard/dashboard_screen.dart new file mode 100644 index 0000000..c7cd8fc --- /dev/null +++ b/android/lib/screens/dashboard/dashboard_screen.dart @@ -0,0 +1,581 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../models/routine.dart'; +import '../../repositories/routine_repository.dart'; +import '../../theme/app_theme.dart'; +import '../routines/widgets/routine_card.dart'; + +class DashboardScreen extends ConsumerWidget { + const DashboardScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final todayRoutinesAsync = ref.watch(todayRoutinesProvider); + + return Scaffold( + body: CustomScrollView( + slivers: [ + // App Bar with greeting + SliverAppBar( + expandedHeight: 120, + floating: true, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + title: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _getGreeting(), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + ), + ), + const Text( + 'Ready to flow?', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + actions: [ + IconButton( + icon: const Icon(Icons.notifications_outlined), + onPressed: () { + // Show notifications + }, + ), + IconButton( + icon: const Icon(Icons.person_outline), + onPressed: () { + // Show profile + }, + ), + ], + ), + + // Daily Progress Ring + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: _buildDailyProgressCard(context), + ), + ), + + // Quick Stats + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: _buildQuickStatsRow(), + ), + ), + + // Today's Routines Header + SliverPadding( + padding: const EdgeInsets.all(16.0), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Today's Routines", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + TextButton( + onPressed: () { + // Navigate to all routines + }, + child: const Text('See All'), + ), + ], + ), + ), + ), + + // Today's Routines List + todayRoutinesAsync.when( + data: (routines) { + if (routines.isEmpty) { + return const SliverFillRemaining( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.spa_outlined, + size: 64, + color: Colors.grey, + ), + SizedBox(height: 16), + Text( + 'No routines for today', + style: TextStyle( + fontSize: 18, + color: Colors.grey, + ), + ), + SizedBox(height: 8), + Text( + 'Add your first routine to get started!', + style: TextStyle( + color: Colors.grey, + ), + ), + ], + ), + ), + ); + } + + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final routine = routines[index]; + return RoutineCard( + routine: routine, + onTap: () => _showRoutineDetails(context, routine), + onComplete: () => _completeRoutine(context, ref, routine), + ); + }, + childCount: routines.length, + ), + ); + }, + loading: () => const SliverFillRemaining( + child: Center(child: CircularProgressIndicator()), + ), + error: (error, stack) => SliverFillRemaining( + child: Center( + child: Text('Error: $error'), + ), + ), + ), + + // Bottom padding + const SliverPadding( + padding: EdgeInsets.only(bottom: 100), + ), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => _showAddRoutineDialog(context), + icon: const Icon(Icons.add), + label: const Text('Add Routine'), + ), + ); + } + + String _getGreeting() { + final hour = DateTime.now().hour; + if (hour < 12) return 'Good morning'; + if (hour < 17) return 'Good afternoon'; + return 'Good evening'; + } + + Widget _buildDailyProgressCard(BuildContext context) { + return Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Row( + children: [ + // Progress Ring + SizedBox( + width: 100, + height: 100, + child: Stack( + fit: StackFit.expand, + children: [ + CircularProgressIndicator( + value: 0.65, // TODO: Calculate from actual data + strokeWidth: 10, + backgroundColor: Colors.grey[200], + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.primary, + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + '65%', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'Done', + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ), + ), + ], + ), + ), + const SizedBox(width: 20), + // Stats + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Today's Progress", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + Text( + '7 of 11 routines completed', + style: TextStyle( + color: Colors.grey[600], + ), + ), + const SizedBox(height: 12), + Row( + children: [ + _buildStatChip( + icon: Icons.local_fire_department, + label: '5 day streak', + color: Colors.orange, + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } + + Widget _buildStatChip({ + required IconData icon, + required String label, + required Color color, + }) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 16, color: color), + const SizedBox(width: 4), + Text( + label, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: color, + ), + ), + ], + ), + ); + } + + Widget _buildQuickStatsRow() { + return Row( + children: [ + Expanded( + child: _buildQuickStatCard( + icon: Icons.water_drop, + label: 'Hydration', + value: '1.5L', + color: Colors.blue, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildQuickStatCard( + icon: Icons.medication, + label: 'Meds Taken', + value: '2/3', + color: Colors.red, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildQuickStatCard( + icon: Icons.star, + label: 'Points', + value: '245', + color: Colors.amber, + ), + ), + ], + ); + } + + Widget _buildQuickStatCard({ + required IconData icon, + required String label, + required String value, + required Color color, + }) { + return Card( + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + Icon(icon, color: color, size: 28), + const SizedBox(height: 8), + Text( + value, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Text( + label, + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ), + ), + ); + } + + void _showRoutineDetails(BuildContext context, Routine routine) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) => DraggableScrollableSheet( + initialChildSize: 0.6, + maxChildSize: 0.9, + minChildSize: 0.4, + expand: false, + builder: (context, scrollController) { + return SingleChildScrollView( + controller: scrollController, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(2), + ), + ), + ), + const SizedBox(height: 20), + Row( + children: [ + Text( + routine.category.icon, + style: const TextStyle(fontSize: 40), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + routine.name, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + Text( + routine.category.displayName, + style: TextStyle( + color: Colors.grey[600], + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 20), + if (routine.description != null) ...[ + Text( + routine.description!, + style: TextStyle( + fontSize: 16, + color: Colors.grey[700], + ), + ), + const SizedBox(height: 20), + ], + _buildDetailRow( + Icons.schedule, + 'Scheduled for', + routine.schedule.time, + ), + _buildDetailRow( + Icons.repeat, + 'Frequency', + _getFrequencyText(routine.schedule), + ), + _buildDetailRow( + Icons.star, + 'Points', + '${routine.points} points', + ), + const SizedBox(height: 30), + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () { + Navigator.pop(context); + // Mark as complete + }, + icon: const Icon(Icons.check_circle), + label: const Text('Mark as Complete'), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + ), + ), + ), + ], + ), + ), + ); + }, + ), + ); + } + + Widget _buildDetailRow(IconData icon, String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + children: [ + Icon(icon, color: Colors.grey), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + Text( + value, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ], + ), + ); + } + + String _getFrequencyText(Schedule schedule) { + switch (schedule.type) { + case ScheduleType.daily: + return 'Every day'; + case ScheduleType.weekly: + final days = schedule.daysOfWeek?.map((d) { + final dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; + return dayNames[d]; + }).join(', '); + return days ?? 'Weekly'; + case ScheduleType.specificDate: + return 'One-time'; + case ScheduleType.interval: + return 'Every ${schedule.intervalDays} days'; + } + } + + void _completeRoutine(BuildContext context, WidgetRef ref, Routine routine) { + // TODO: Implement completion logic + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('${routine.name} completed! +${routine.points} points'), + backgroundColor: Colors.green, + behavior: SnackBarBehavior.floating, + action: SnackBarAction( + label: 'UNDO', + onPressed: () { + // Undo completion + }, + ), + ), + ); + } + + void _showAddRoutineDialog(BuildContext context) { + // TODO: Navigate to add routine screen + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => const AddRoutineSheet(), + ); + } +} + +class AddRoutineSheet extends StatelessWidget { + const AddRoutineSheet({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Add New Routine', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 20), + ...RoutineCategory.values.map((category) => ListTile( + leading: Text(category.icon, style: const TextStyle(fontSize: 28)), + title: Text(category.displayName), + onTap: () { + // Navigate to routine creation with this category + Navigator.pop(context); + }, + )), + ], + ), + ); + } +} diff --git a/android/lib/theme/app_theme.dart b/android/lib/theme/app_theme.dart new file mode 100644 index 0000000..70915a4 --- /dev/null +++ b/android/lib/theme/app_theme.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; + +class AppTheme { + // Primary Colors + static const Color primary = Color(0xFF4A90E2); + static const Color primaryLight = Color(0xFF7BB3F0); + static const Color primaryDark = Color(0xFF2E5C8A); + + // Category Colors + static const Color medication = Color(0xFFE74C3C); + static const Color vitamin = Color(0xFFF39C12); + static const Color appointment = Color(0xFF9B59B6); + static const Color sleep = Color(0xFF8E44AD); + static const Color food = Color(0xFFE67E22); + static const Color hydration = Color(0xFF3498DB); + static const Color exercise = Color(0xFF27AE60); + static const Color hygiene = Color(0xFF1ABC9C); + static const Color selfCare = Color(0xFF16A085); + + // Gamification Colors + static const Color gold = Color(0xFFFFD700); + static const Color silver = Color(0xFFC0C0C0); + static const Color bronze = Color(0xFFCD7F32); + + // Semantic Colors + static const Color success = Color(0xFF27AE60); + static const Color warning = Color(0xFFF39C12); + static const Color error = Color(0xFFE74C3C); + static const Color info = Color(0xFF3498DB); + + static Color getCategoryColor(RoutineCategory category) { + switch (category) { + case RoutineCategory.medication: + return medication; + case RoutineCategory.vitamin: + return vitamin; + case RoutineCategory.appointment: + return appointment; + case RoutineCategory.sleep: + return sleep; + case RoutineCategory.food: + return food; + case RoutineCategory.hydration: + return hydration; + case RoutineCategory.exercise: + return exercise; + case RoutineCategory.hygiene: + return hygiene; + case RoutineCategory.selfCare: + return selfCare; + case RoutineCategory.custom: + return primary; + } + } +} + +// Temporary enum for compilation +enum RoutineCategory { + medication, + vitamin, + appointment, + sleep, + food, + hydration, + exercise, + hygiene, + selfCare, + custom, +} + +class AppTheme { + static ThemeData get lightTheme { + return ThemeData( + useMaterial3: true, + brightness: Brightness.light, + colorScheme: const ColorScheme.light( + primary: AppColors.primary, + secondary: AppColors.primaryLight, + surface: Colors.white, + background: Color(0xFFF8F9FA), + onPrimary: Colors.white, + onSecondary: Colors.white, + onSurface: Color(0xFF1A1A2E), + onBackground: Color(0xFF1A1A2E), + ), + scaffoldBackgroundColor: const Color(0xFFF8F9FA), + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: false, + backgroundColor: Colors.transparent, + foregroundColor: Color(0xFF1A1A2E), + titleTextStyle: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Color(0xFF1A1A2E), + ), + ), + cardTheme: CardTheme( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + bottomNavigationBarTheme: const BottomNavigationBarThemeData( + backgroundColor: Colors.white, + selectedItemColor: AppColors.primary, + unselectedItemColor: Colors.grey, + type: BottomNavigationBarType.fixed, + elevation: 8, + ), + floatingActionButtonTheme: FloatingActionButtonThemeData( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: Colors.grey[100], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.primary, width: 2), + ), + ), + ); + } + + static ThemeData get darkTheme { + return ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: const ColorScheme.dark( + primary: AppColors.primaryLight, + secondary: AppColors.primary, + surface: Color(0xFF1E1E2E), + background: Color(0xFF0F0F1A), + onPrimary: Colors.white, + onSecondary: Colors.white, + onSurface: Colors.white, + onBackground: Colors.white, + ), + scaffoldBackgroundColor: const Color(0xFF0F0F1A), + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: false, + backgroundColor: Colors.transparent, + foregroundColor: Colors.white, + titleTextStyle: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + cardTheme: CardTheme( + elevation: 2, + color: const Color(0xFF1E1E2E), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + bottomNavigationBarTheme: const BottomNavigationBarThemeData( + backgroundColor: Color(0xFF1E1E2E), + selectedItemColor: AppColors.primaryLight, + unselectedItemColor: Colors.grey, + type: BottomNavigationBarType.fixed, + elevation: 8, + ), + ); + } +} diff --git a/android/pubspec.yaml b/android/pubspec.yaml new file mode 100644 index 0000000..0e9328e --- /dev/null +++ b/android/pubspec.yaml @@ -0,0 +1,88 @@ +name: lifeflow +description: Transform your daily routines into enjoyable habits + +publish_to: 'none' + +version: 1.0.0+1 + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + flutter: + sdk: flutter + + # State Management + flutter_riverpod: ^2.4.0 + riverpod_annotation: ^2.2.0 + + # Local Database + hive: ^2.2.3 + hive_flutter: ^1.1.0 + + # Notifications + flutter_local_notifications: ^16.1.0 + timezone: ^0.9.2 + + # UI Components + cupertino_icons: ^1.0.6 + flutter_svg: ^2.0.9 + shimmer: ^3.0.0 + flutter_slidable: ^3.0.1 + fl_chart: ^0.65.0 + percent_indicator: ^4.2.3 + confetti: ^0.7.0 + + # Date/Time + intl: ^0.18.1 + jiffy: ^6.2.1 + + # Utilities + uuid: ^4.2.1 + equatable: ^2.0.5 + freezed_annotation: ^2.4.1 + json_annotation: ^4.8.1 + + # SMS (Android only) + telephony: ^0.2.0 + permission_handler: ^11.0.1 + + # Backend (Phase 2) + # firebase_core: ^2.21.0 + # firebase_auth: ^4.12.1 + # cloud_firestore: ^4.12.2 + # firebase_messaging: ^14.7.3 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.1 + + # Code Generation + build_runner: ^2.4.7 + freezed: ^2.4.5 + json_serializable: ^6.7.1 + riverpod_generator: ^2.3.5 + hive_generator: ^2.0.1 + + # Testing + mockito: ^5.4.4 + +flutter: + uses-material-design: true + + assets: + - assets/icons/ + - assets/images/ + - assets/animations/ + + fonts: + - family: Inter + fonts: + - asset: assets/fonts/Inter-Regular.ttf + - asset: assets/fonts/Inter-Medium.ttf + weight: 500 + - asset: assets/fonts/Inter-SemiBold.ttf + weight: 600 + - asset: assets/fonts/Inter-Bold.ttf + weight: 700 diff --git a/docs/DEVELOPMENT_PLAN.md b/docs/DEVELOPMENT_PLAN.md new file mode 100644 index 0000000..2245704 --- /dev/null +++ b/docs/DEVELOPMENT_PLAN.md @@ -0,0 +1,444 @@ +# LifeFlow Development Plan + +**Project:** LifeFlow - Personal Life Management System +**Start Date:** 2026-02-14 +**Target:** MVP in 2 weeks, Full App in 6 weeks + +--- + +## Architecture Overview + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ LifeFlow System โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Android App โ”‚ PC App โ”‚ Web Dashboard โ”‚ HA Add-on โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Shared Backend (Firebase/Supabase) โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ AI Services โ”‚ SMS Parser โ”‚ Notifications โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## Phase 1: Android MVP (Week 1-2) + +### Week 1: Foundation +**Day 1-2: Project Setup** +- [ ] Flutter project initialization +- [ ] Architecture setup (Riverpod + Hive) +- [ ] Theme system (Dark/Light/Calm) +- [ ] Navigation structure + +**Day 3-4: Core Data Models** +- [ ] User model +- [ ] Routine model +- [ ] Activity log model +- [ ] Local database (Hive) + +**Day 5-7: Basic UI Screens** +- [ ] Dashboard screen +- [ ] Routine list screen +- [ ] Add/Edit routine screen +- [ ] Simple settings + +### Week 2: Features + Polish +**Day 8-10: Core Features** +- [ ] Routine CRUD operations +- [ ] Local notifications +- [ ] Basic streak tracking +- [ ] Activity logging + +**Day 11-12: Gamification MVP** +- [ ] Points system +- [ ] Basic badges +- [ ] Streak visualization +- [ ] Daily progress ring + +**Day 13-14: Polish + Testing** +- [ ] Animations +- [ ] Error handling +- [ ] Onboarding flow +- [ ] Beta build + +--- + +## Phase 2: Smart Features (Week 3-4) + +### Week 3: Intelligence +**SMS Parsing** +- [ ] SMS permission handling +- [ ] Pattern matching engine +- [ ] Appointment extraction +- [ ] Medication detection +- [ ] Manual confirmation UI + +**AI Suggestions** +- [ ] Routine recommendation engine +- [ ] Optimal timing suggestions +- [ ] Pattern recognition +- [ ] Smart reminders + +### Week 4: Integration +**Backend Setup** +- [ ] Firebase/Supabase project +- [ ] Authentication +- [ ] Cloud sync +- [ ] Conflict resolution + +**Widgets** +- [ ] Home screen widget +- [ ] Quick actions +- [ ] Lock screen integration + +--- + +## Phase 3: Ecosystem (Week 5-6) + +### Week 5: PC App +- [ ] Flutter desktop build +- [ ] System tray integration +- [ ] Global hotkeys +- [ ] Quick log overlay +- [ ] Data sync + +### Week 6: Home Assistant +- [ ] Custom component +- [ ] MQTT integration +- [ ] Sensors + Buttons +- [ ] Automations examples +- [ ] Dashboard cards + +--- + +## Technical Stack + +### Android App +| Layer | Technology | +|-------|-----------| +| Framework | Flutter 3.x | +| State | Riverpod | +| Local DB | Hive | +| Remote DB | Firebase/Firestore | +| Auth | Firebase Auth | +| Notifications | flutter_local_notifications | +| SMS | telephony | +| Analytics | Firebase Analytics | + +### Backend +| Service | Technology | +|---------|-----------| +| Database | Cloud Firestore | +| Auth | Firebase Auth | +| Functions | Cloud Functions (Node.js) | +| Storage | Firebase Storage | +| ML | Firebase ML Kit | + +### PC App +| Feature | Implementation | +|---------|---------------| +| Framework | Flutter Desktop | +| System Tray | system_tray | +| Hotkeys | hotkey_manager | +| Notifications | local_notifier | + +### Home Assistant +| Component | Type | +|-----------|------| +| Main Integration | Custom Component | +| Communication | MQTT / REST API | +| Entities | Sensors, Buttons, Switches | + +--- + +## Data Models + +### User +```dart +class User { + String id; + String email; + String name; + String? avatarUrl; + UserSettings settings; + GamificationStats stats; + DateTime createdAt; +} +``` + +### Routine +```dart +class Routine { + String id; + String name; + String? description; + RoutineCategory category; + Schedule schedule; + List reminders; + int points; + String? icon; + String? color; + bool isActive; + DateTime createdAt; +} + +enum RoutineCategory { + medication, + vitamin, + appointment, + sleep, + food, + hydration, + exercise, + hygiene, + selfCare, + custom +} +``` + +### Activity +```dart +class Activity { + String id; + String routineId; + DateTime timestamp; + bool completed; + String? notes; + Mood? mood; + int pointsEarned; +} +``` + +### Gamification +```dart +class GamificationStats { + int totalPoints; + int currentStreak; + int longestStreak; + int level; + List unlockedBadges; + Map categoryStreaks; +} +``` + +--- + +## UI/UX Design System + +### Colors +```dart +class AppColors { + // Primary + static const primary = Color(0xFF4A90E2); + static const primaryLight = Color(0xFF7BB3F0); + static const primaryDark = Color(0xFF2E5C8A); + + // Categories + static const medication = Color(0xFFE74C3C); + static const vitamin = Color(0xFFF39C12); + static const hydration = Color(0xFF3498DB); + static const exercise = Color(0xFF27AE60); + static const sleep = Color(0xFF9B59B6); + static const food = Color(0xFFE67E22); + + // Gamification + static const gold = Color(0xFFFFD700); + static const silver = Color(0xFFC0C0C0); + static const bronze = Color(0xFFCD7F32); +} +``` + +### Typography +```dart +class AppTextStyles { + static const heading1 = TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + letterSpacing: -0.5, + ); + + static const heading2 = TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + letterSpacing: -0.3, + ); + + static const body = TextStyle( + fontSize: 16, + fontWeight: FontWeight.normal, + letterSpacing: 0, + ); + + static const caption = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.grey, + ); +} +``` + +--- + +## Feature Specifications + +### 1. Dashboard +**Layout:** +- Greeting with user name +- Daily progress ring (overall completion) +- Today's routines (grouped by time) +- Current streak flame +- Points display +- Quick add buttons + +**Interactions:** +- Swipe routine โ†’ Mark done +- Tap routine โ†’ Details/Edit +- Pull down โ†’ Refresh +- Long press โ†’ Quick actions + +### 2. Routine Detail +**Layout:** +- Icon + Name + Category +- Schedule info +- Streak counter +- History list +- Statistics + +**Actions:** +- Log completion +- Edit routine +- Pause/Resume +- Delete +- Share + +### 3. Add Routine +**Steps:** +1. Select category +2. Enter name/description +3. Set schedule (daily/weekly/custom) +4. Add reminders +5. Assign points +6. Choose icon/color + +**Smart Features:** +- Template suggestions +- Category-based defaults +- Icon recommendations + +### 4. Notifications +**Types:** +- Scheduled reminders +- Streak at risk +- Achievement unlocked +- Refill alerts +- Prep reminders + +**Actions:** +- Mark done (from notification) +- Snooze (5/15/30 min) +- Dismiss +- View details + +--- + +## SMS Parsing Patterns + +### Appointment Pattern +```regex +(appointment|meeting|consultation).{0,50}(\w+ \d{1,2}).{0,20}(\d{1,2}:\d{2}).{0,50}(at|@)\s+([\w\s]+) +``` + +### Medication Pattern +```regex +(prescription|medication|refill).{0,30}(ready|available|pickup).{0,50}(\d+).{0,20}(of|out of).{0,5}(\d+) +``` + +### Meeting Pattern +```regex +(meeting|standup|sync).{0,30}(tomorrow|today|(?:\w+day)).{0,20}(\d{1,2}(?::\d{2})?\s*(?:AM|PM)?).{0,50}(in|at)\s+([\w\s]+) +``` + +--- + +## Testing Strategy + +### Unit Tests +- Data models +- Business logic +- SMS parsing +- Gamification calculations + +### Widget Tests +- Screen rendering +- User interactions +- Form validation +- State changes + +### Integration Tests +- Database operations +- Notification flow +- Sync mechanism +- SMS parsing + +### E2E Tests +- Complete user flows +- Critical paths +- Edge cases + +--- + +## Deployment Plan + +### Android +1. Internal Testing (closed) +2. Closed Testing (invite-only) +3. Open Testing (public beta) +4. Production Release + +### PC App +1. Windows (installer) +2. macOS (DMG) +3. Linux (AppImage) + +### Home Assistant +1. HACS (Home Assistant Community Store) +2. Official integration (if popular) + +--- + +## Success Metrics + +| Metric | Target | +|--------|--------| +| Daily Active Users | 1000+ (Month 3) | +| Routine Completion Rate | 70%+ | +| 7-Day Retention | 40%+ | +| Average Streak | 5+ days | +| App Store Rating | 4.5+ | + +--- + +## Next Steps + +### Immediate (Today) +1. Initialize Flutter project +2. Set up architecture +3. Create data models +4. Build basic UI + +### This Week +1. Complete MVP features +2. Add gamification +3. Test on device +4. Prepare beta + +### Next Week +1. Add SMS parsing +2. Implement AI suggestions +3. Connect backend +4. Beta testing + +--- + +Ready to start building! ๐Ÿš€