feat: Implement husband features and fix iOS Safari web startup

Implement initial features for husband's companion app, including mock data
service and husband notes screen. Refactor scripture and cycle services
for improved stability and testability. Address iOS Safari web app
startup issue by removing deprecated initialization.

- Implemented MockDataService and HusbandNotesScreen.
- Converted _DashboardTab and DevotionalScreen to StatefulWidgets for robust
  scripture provider initialization.
- Refactored CycleService to use immutable CycleInfo class, reducing UI rebuilds.
- Removed deprecated window.flutterConfiguration from index.html, resolving
  Flutter web app startup failure on iOS Safari.
- Updated and fixed related tests.
This commit is contained in:
2025-12-26 22:40:52 -06:00
parent 464692ce56
commit b4b2bfe749
47 changed files with 240110 additions and 2578 deletions

View File

@@ -10,25 +10,25 @@ class AppColors {
static const Color lavender = Color(0xFFD4C4E8);
static const Color cream = Color(0xFFFDF8F5);
static const Color softGold = Color(0xFFD4A574);
// Text Colors
static const Color charcoal = Color(0xFF3D3D3D);
static const Color warmGray = Color(0xFF7A7A7A);
static const Color lightGray = Color(0xFFB8B8B8);
// Husband's App Colors
static const Color navyBlue = Color(0xFF2C3E50);
static const Color steelBlue = Color(0xFF5D7B93);
static const Color warmCream = Color(0xFFF5F0E8);
static const Color gold = Color(0xFFC9A961);
static const Color softCoral = Color(0xFFE8B4A8);
// Phase Colors
static const Color menstrualPhase = Color(0xFFE88A9E);
static const Color follicularPhase = Color(0xFF8BC5A3);
static const Color ovulationPhase = Color(0xFFB8A5D4);
static const Color lutealPhase = Color(0xFF8BA5C5);
// Semantic Colors
static const Color success = Color(0xFF7AB98A);
static const Color warning = Color(0xFFE8C567);
@@ -42,7 +42,7 @@ class AppTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.light,
// Color Scheme
colorScheme: const ColorScheme.light(
primary: AppColors.sageGreen,
@@ -54,10 +54,10 @@ class AppTheme {
onSecondary: Colors.white,
onSurface: AppColors.charcoal,
),
// Scaffold
scaffoldBackgroundColor: AppColors.cream,
// AppBar
appBarTheme: AppBarTheme(
backgroundColor: AppColors.cream,
@@ -70,7 +70,7 @@ class AppTheme {
color: AppColors.charcoal,
),
),
// Text Theme
textTheme: TextTheme(
displayLarge: GoogleFonts.outfit(
@@ -124,9 +124,9 @@ class AppTheme {
color: AppColors.charcoal,
),
),
// Card Theme
cardTheme: CardTheme(
cardTheme: CardThemeData(
color: Colors.white,
elevation: 2,
shadowColor: AppColors.charcoal.withOpacity(0.1),
@@ -135,7 +135,7 @@ class AppTheme {
),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
// Button Themes
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
@@ -152,7 +152,7 @@ class AppTheme {
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.sageGreen,
@@ -167,7 +167,7 @@ class AppTheme {
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.rose,
@@ -177,7 +177,7 @@ class AppTheme {
),
),
),
// Input Decoration
inputDecorationTheme: InputDecorationTheme(
filled: true,
@@ -194,13 +194,14 @@ class AppTheme {
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.sageGreen, width: 2),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
hintStyle: GoogleFonts.outfit(
color: AppColors.lightGray,
fontSize: 14,
),
),
// Bottom Navigation
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: Colors.white,
@@ -217,14 +218,14 @@ class AppTheme {
fontWeight: FontWeight.w400,
),
),
// Floating Action Button
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: AppColors.sageGreen,
foregroundColor: Colors.white,
elevation: 4,
),
// Slider Theme
sliderTheme: SliderThemeData(
activeTrackColor: AppColors.sageGreen,
@@ -233,7 +234,7 @@ class AppTheme {
overlayColor: AppColors.sageGreen.withOpacity(0.2),
trackHeight: 4,
),
// Divider
dividerTheme: DividerThemeData(
color: AppColors.lightGray.withOpacity(0.3),
@@ -242,24 +243,12 @@ class AppTheme {
),
);
}
static ThemeData get darkTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
<<<<<<< HEAD
colorScheme: const ColorScheme.dark(
primary: AppColors.sageGreen,
secondary: AppColors.rose,
tertiary: AppColors.lavender,
surface: Color(0xFF1E1E1E),
error: AppColors.error,
),
scaffoldBackgroundColor: const Color(0xFF121212),
=======
// Color Scheme
colorScheme: ColorScheme.dark(
primary: AppColors.sageGreen,
@@ -273,10 +262,10 @@ class AppTheme {
onSurfaceVariant: Colors.white70,
outline: Colors.white.withOpacity(0.1),
),
// Scaffold
scaffoldBackgroundColor: const Color(0xFF121212),
// AppBar
appBarTheme: AppBarTheme(
backgroundColor: const Color(0xFF121212),
@@ -289,17 +278,14 @@ class AppTheme {
color: Colors.white,
),
),
// Text Theme
>>>>>>> 6742220 (Your commit message here)
textTheme: TextTheme(
displayLarge: GoogleFonts.outfit(
fontSize: 32,
fontWeight: FontWeight.w600,
color: Colors.white,
),
<<<<<<< HEAD
=======
displayMedium: GoogleFonts.outfit(
fontSize: 28,
fontWeight: FontWeight.w600,
@@ -325,7 +311,6 @@ class AppTheme {
fontWeight: FontWeight.w500,
color: Colors.white,
),
>>>>>>> 6742220 (Your commit message here)
bodyLarge: GoogleFonts.outfit(
fontSize: 16,
fontWeight: FontWeight.w400,
@@ -336,16 +321,6 @@ class AppTheme {
fontWeight: FontWeight.w400,
color: Colors.white70,
),
<<<<<<< HEAD
),
cardTheme: CardTheme(
color: const Color(0xFF1E1E1E),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
=======
bodySmall: GoogleFonts.outfit(
fontSize: 12,
fontWeight: FontWeight.w400,
@@ -357,9 +332,9 @@ class AppTheme {
color: Colors.white,
),
),
// Card Theme
cardTheme: CardTheme(
cardTheme: CardThemeData(
color: const Color(0xFF1E1E1E),
elevation: 0, // Material 3 uses color/opacity for elevation in dark mode
shadowColor: Colors.transparent,
@@ -418,13 +393,14 @@ class AppTheme {
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.sageGreen, width: 2),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
hintStyle: GoogleFonts.outfit(
color: Colors.white38,
fontSize: 14,
),
),
// Bottom Navigation
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: const Color(0xFF1E1E1E),
@@ -459,7 +435,6 @@ class AppTheme {
color: Colors.white.withOpacity(0.05),
thickness: 1,
space: 24,
>>>>>>> 6742220 (Your commit message here)
),
);
}
@@ -467,35 +442,21 @@ class AppTheme {
/// Scripture text style
TextStyle scriptureStyle(BuildContext context, {double? fontSize}) {
<<<<<<< HEAD
return GoogleFonts.lora(
fontSize: fontSize ?? 16,
fontStyle: FontStyle.italic,
color: AppColors.charcoal,
=======
final isDark = Theme.of(context).brightness == Brightness.dark;
return GoogleFonts.lora(
fontSize: fontSize ?? 16,
fontStyle: FontStyle.italic,
color: isDark ? Colors.white : AppColors.charcoal,
>>>>>>> 6742220 (Your commit message here)
height: 1.6,
);
}
/// Scripture reference style
TextStyle scriptureRefStyle(BuildContext context) {
<<<<<<< HEAD
return GoogleFonts.outfit(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.warmGray,
=======
final isDark = Theme.of(context).brightness == Brightness.dark;
return GoogleFonts.outfit(
fontSize: 12,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white54 : AppColors.warmGray,
>>>>>>> 6742220 (Your commit message here)
);
}