feat: Complete Flutter app structure for LifeFlow
ADDED: - analysis_options.yaml (Flutter linting rules) - .gitignore (Flutter-specific) - All missing screen files: - routines/routines_screen.dart - calendar/calendar_screen.dart - stats/stats_screen.dart - settings/settings_screen.dart - RoutineCard widget with category colors - Models barrel export (models.dart) - Fixed AppTheme (removed duplicate class) APP STRUCTURE: ├── lib/ │ ├── main.dart (entry point with navigation) │ ├── models/ (Routine, Activity, Gamification) │ ├── repositories/ (RoutineRepository with Riverpod) │ ├── screens/ (Dashboard, Routines, Calendar, Stats, Settings) │ └── theme/ (AppTheme, AppColors) │ FEATURES READY: - Bottom navigation (5 tabs) - Dashboard with progress ring - Routine cards with category icons - Settings screen with options - Light/Dark theme support - Riverpod state management setup NOTE: This is a skeleton app that compiles but needs: - Hive initialization - Actual data persistence - Notification setup - More screen implementations To build APK, run: flutter build apk --release
This commit is contained in:
parent
545e092d3b
commit
a21873ebc3
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Flutter/Dart
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
|
/android/key.properties
|
||||||
|
/ios/Flutter/.last_build_id
|
||||||
|
/ios/Pods/
|
||||||
|
/macos/Flutter/ephemeral/
|
||||||
|
/windows/flutter/ephemeral/
|
||||||
|
/linux/flutter/ephemeral/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Secrets
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
|
key.properties
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Test coverage
|
||||||
|
coverage/
|
||||||
|
.test_coverage.dart
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
avoid_print: false
|
||||||
|
prefer_single_quotes: true
|
||||||
|
prefer_const_constructors: true
|
||||||
|
prefer_const_literals_to_create_immutables: true
|
||||||
|
avoid_unnecessary_containers: true
|
||||||
|
use_key_in_widget_constructors: true
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export 'routine.dart';
|
||||||
|
export 'activity.dart';
|
||||||
|
export 'gamification.dart';
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CalendarScreen extends StatelessWidget {
|
||||||
|
const CalendarScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Calendar'),
|
||||||
|
),
|
||||||
|
body: const Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.calendar_today, size: 64, color: Colors.grey),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Calendar view coming soon',
|
||||||
|
style: TextStyle(fontSize: 18, color: Colors.grey),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class RoutinesScreen extends StatelessWidget {
|
||||||
|
const RoutinesScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('My Routines'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.filter_list),
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: const Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.checklist, size: 64, color: Colors.grey),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Your routines will appear here',
|
||||||
|
style: TextStyle(fontSize: 18, color: Colors.grey),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Tap + to add your first routine',
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../models/routine.dart';
|
||||||
|
|
||||||
|
class RoutineCard extends StatelessWidget {
|
||||||
|
final Routine routine;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final VoidCallback? onComplete;
|
||||||
|
|
||||||
|
const RoutineCard({
|
||||||
|
super.key,
|
||||||
|
required this.routine,
|
||||||
|
this.onTap,
|
||||||
|
this.onComplete,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// Icon
|
||||||
|
Container(
|
||||||
|
width: 56,
|
||||||
|
height: 56,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _getCategoryColor().withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
routine.category.icon,
|
||||||
|
style: const TextStyle(fontSize: 28),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
// Info
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
routine.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'${routine.schedule.time} · ${routine.category.displayName}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (routine.description != null && routine.description!.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
routine.description!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey[500],
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Complete button
|
||||||
|
if (onComplete != null)
|
||||||
|
IconButton(
|
||||||
|
onPressed: onComplete,
|
||||||
|
icon: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _getCategoryColor().withOpacity(0.1),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: _getCategoryColor(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getCategoryColor() {
|
||||||
|
return routine.category.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension for category colors
|
||||||
|
extension CategoryColor on RoutineCategory {
|
||||||
|
Color get color {
|
||||||
|
switch (this) {
|
||||||
|
case RoutineCategory.medication:
|
||||||
|
return const Color(0xFFE74C3C);
|
||||||
|
case RoutineCategory.vitamin:
|
||||||
|
return const Color(0xFFF39C12);
|
||||||
|
case RoutineCategory.appointment:
|
||||||
|
return const Color(0xFF9B59B6);
|
||||||
|
case RoutineCategory.sleep:
|
||||||
|
return const Color(0xFF8E44AD);
|
||||||
|
case RoutineCategory.food:
|
||||||
|
return const Color(0xFFE67E22);
|
||||||
|
case RoutineCategory.hydration:
|
||||||
|
return const Color(0xFF3498DB);
|
||||||
|
case RoutineCategory.exercise:
|
||||||
|
return const Color(0xFF27AE60);
|
||||||
|
case RoutineCategory.hygiene:
|
||||||
|
return const Color(0xFF1ABC9C);
|
||||||
|
case RoutineCategory.selfCare:
|
||||||
|
return const Color(0xFF16A085);
|
||||||
|
case RoutineCategory.custom:
|
||||||
|
return const Color(0xFF4A90E2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SettingsScreen extends StatelessWidget {
|
||||||
|
const SettingsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Settings'),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.person),
|
||||||
|
title: const Text('Account'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.notifications),
|
||||||
|
title: const Text('Notifications'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.palette),
|
||||||
|
title: const Text('Appearance'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.backup),
|
||||||
|
title: const Text('Backup & Sync'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.privacy_tip),
|
||||||
|
title: const Text('Privacy'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.help),
|
||||||
|
title: const Text('Help & Support'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.info),
|
||||||
|
title: const Text('About'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class StatsScreen extends StatelessWidget {
|
||||||
|
const StatsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Statistics'),
|
||||||
|
),
|
||||||
|
body: const Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.insights, size: 64, color: Colors.grey),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Your stats will appear here',
|
||||||
|
style: TextStyle(fontSize: 18, color: Colors.grey),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AppTheme {
|
class AppColors {
|
||||||
// Primary Colors
|
// Primary Colors
|
||||||
static const Color primary = Color(0xFF4A90E2);
|
static const Color primary = Color(0xFF4A90E2);
|
||||||
static const Color primaryLight = Color(0xFF7BB3F0);
|
static const Color primaryLight = Color(0xFF7BB3F0);
|
||||||
|
|
@ -27,45 +27,6 @@ class AppTheme {
|
||||||
static const Color warning = Color(0xFFF39C12);
|
static const Color warning = Color(0xFFF39C12);
|
||||||
static const Color error = Color(0xFFE74C3C);
|
static const Color error = Color(0xFFE74C3C);
|
||||||
static const Color info = Color(0xFF3498DB);
|
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 {
|
class AppTheme {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue