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:
@@ -4,11 +4,7 @@ import 'dart:math' as math;
|
||||
import '../theme/app_theme.dart';
|
||||
import '../models/cycle_entry.dart';
|
||||
|
||||
<<<<<<< HEAD
|
||||
class CycleRing extends StatelessWidget {
|
||||
=======
|
||||
class CycleRing extends StatefulWidget {
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
final int dayOfCycle;
|
||||
final int totalDays;
|
||||
final CyclePhase phase;
|
||||
@@ -21,77 +17,11 @@ class CycleRing extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
<<<<<<< HEAD
|
||||
Widget build(BuildContext context) {
|
||||
final progress = dayOfCycle / totalDays;
|
||||
final daysUntilNextPeriod = totalDays - dayOfCycle;
|
||||
|
||||
return Container(
|
||||
width: 220,
|
||||
height: 220,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Background ring
|
||||
CustomPaint(
|
||||
size: const Size(220, 220),
|
||||
painter: _CycleRingPainter(
|
||||
progress: progress,
|
||||
phase: phase,
|
||||
),
|
||||
),
|
||||
|
||||
// Center content
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Day $dayOfCycle',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(phase).withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
phase.emoji,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
phase.label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _getPhaseColor(phase),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
daysUntilNextPeriod > 0
|
||||
? '$daysUntilNextPeriod days until period'
|
||||
: 'Period expected',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
color: AppColors.warmGray,
|
||||
=======
|
||||
State<CycleRing> createState() => _CycleRingState();
|
||||
}
|
||||
|
||||
class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMixin {
|
||||
class _CycleRingState extends State<CycleRing>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _animation;
|
||||
|
||||
@@ -125,7 +55,7 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
|
||||
animation: _animation,
|
||||
builder: (context, child) {
|
||||
final currentProgress = targetProgress * _animation.value;
|
||||
|
||||
|
||||
return SizedBox(
|
||||
width: 220,
|
||||
height: 220,
|
||||
@@ -141,7 +71,7 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
|
||||
isDark: isDark,
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
// Center content with scale and fade animation
|
||||
Transform.scale(
|
||||
scale: 0.8 + (0.2 * _animation.value),
|
||||
@@ -152,19 +82,28 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
|
||||
children: [
|
||||
Text(
|
||||
'Day ${widget.dayOfCycle}',
|
||||
style: Theme.of(context).textTheme.displayMedium?.copyWith(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.displayMedium
|
||||
?.copyWith(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(widget.phase).withOpacity(isDark ? 0.3 : 0.2),
|
||||
color: _getPhaseColor(widget.phase)
|
||||
.withOpacity(isDark ? 0.3 : 0.2),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: isDark ? Border.all(color: _getPhaseColor(widget.phase).withOpacity(0.5)) : null,
|
||||
border: isDark
|
||||
? Border.all(
|
||||
color: _getPhaseColor(widget.phase)
|
||||
.withOpacity(0.5))
|
||||
: null,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -179,7 +118,9 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: isDark ? Colors.white : _getPhaseColor(widget.phase),
|
||||
color: isDark
|
||||
? Colors.white
|
||||
: _getPhaseColor(widget.phase),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -191,24 +132,19 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
|
||||
? '$daysUntilNextPeriod days until period'
|
||||
: 'Period expected',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
fontSize: 12,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
],
|
||||
),
|
||||
=======
|
||||
);
|
||||
},
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -229,18 +165,13 @@ class _CycleRingState extends State<CycleRing> with SingleTickerProviderStateMix
|
||||
class _CycleRingPainter extends CustomPainter {
|
||||
final double progress;
|
||||
final CyclePhase phase;
|
||||
<<<<<<< HEAD
|
||||
|
||||
_CycleRingPainter({required this.progress, required this.phase});
|
||||
=======
|
||||
final bool isDark;
|
||||
|
||||
_CycleRingPainter({
|
||||
required this.progress,
|
||||
required this.progress,
|
||||
required this.phase,
|
||||
required this.isDark,
|
||||
});
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
@@ -250,11 +181,8 @@ class _CycleRingPainter extends CustomPainter {
|
||||
|
||||
// Background arc
|
||||
final bgPaint = Paint()
|
||||
<<<<<<< HEAD
|
||||
..color = AppColors.lightGray.withOpacity(0.2)
|
||||
=======
|
||||
..color = (isDark ? Colors.white : AppColors.lightGray).withOpacity(isDark ? 0.05 : 0.1)
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
..color =
|
||||
(isDark ? Colors.white : AppColors.lightGray).withOpacity(isDark ? 0.05 : 0.1)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round;
|
||||
@@ -287,11 +215,7 @@ class _CycleRingPainter extends CustomPainter {
|
||||
final dotY = center.dy + radius * math.sin(dotAngle);
|
||||
|
||||
final dotPaint = Paint()
|
||||
<<<<<<< HEAD
|
||||
..color = Colors.white
|
||||
=======
|
||||
..color = isDark ? const Color(0xFF1E1E1E) : Colors.white
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
final dotBorderPaint = Paint()
|
||||
@@ -308,11 +232,19 @@ class _CycleRingPainter extends CustomPainter {
|
||||
case CyclePhase.menstrual:
|
||||
return [AppColors.rose, AppColors.menstrualPhase, AppColors.blushPink];
|
||||
case CyclePhase.follicular:
|
||||
return [AppColors.sageGreen, AppColors.follicularPhase, AppColors.sageGreen.withOpacity(0.7)];
|
||||
return [
|
||||
AppColors.sageGreen,
|
||||
AppColors.follicularPhase,
|
||||
AppColors.sageGreen.withOpacity(0.7)
|
||||
];
|
||||
case CyclePhase.ovulation:
|
||||
return [AppColors.lavender, AppColors.ovulationPhase, AppColors.rose];
|
||||
case CyclePhase.luteal:
|
||||
return [AppColors.lutealPhase, AppColors.lavender, AppColors.blushPink];
|
||||
return [
|
||||
AppColors.lutealPhase,
|
||||
AppColors.lavender,
|
||||
AppColors.blushPink
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,43 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
<<<<<<< HEAD
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import '../screens/log/log_screen.dart';
|
||||
|
||||
class QuickLogButtons extends StatelessWidget {
|
||||
const QuickLogButtons({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
_buildQuickButton(
|
||||
icon: Icons.water_drop_outlined,
|
||||
label: 'Period',
|
||||
color: AppColors.menstrualPhase,
|
||||
onTap: () => _navigateToLog(context),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildQuickButton(
|
||||
icon: Icons.emoji_emotions_outlined,
|
||||
label: 'Mood',
|
||||
color: AppColors.softGold,
|
||||
onTap: () => _navigateToLog(context),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildQuickButton(
|
||||
icon: Icons.flash_on_outlined,
|
||||
label: 'Energy',
|
||||
color: AppColors.follicularPhase,
|
||||
onTap: () => _navigateToLog(context),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildQuickButton(
|
||||
icon: Icons.healing_outlined,
|
||||
label: 'Symptoms',
|
||||
color: AppColors.lavender,
|
||||
onTap: () => _navigateToLog(context),
|
||||
=======
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
@@ -80,32 +41,11 @@ class QuickLogButtons extends ConsumerWidget {
|
||||
label: 'Symptoms',
|
||||
color: AppColors.lavender,
|
||||
onTap: () => _navigateToLog(ref),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
void _navigateToLog(BuildContext context) {
|
||||
// Navigate to the Log tab (index 2) of HomeScreen if possible,
|
||||
// but since we are inside a tab, we can't easily switch the parent tab index without context. Using a provider or callback would be best.
|
||||
// For now, let's push the LogScreen as a new route for "Quick Log" feel.
|
||||
// Ideally we would switch the BottomNavBar index.
|
||||
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => const Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: Size.fromHeight(0),
|
||||
child: SizedBox.shrink()
|
||||
),
|
||||
body: LogScreen()
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuickButton({
|
||||
=======
|
||||
void _navigateToLog(WidgetRef ref) {
|
||||
// Navigate to the Log tab (index 2)
|
||||
ref.read(navigationProvider.notifier).setIndex(2);
|
||||
@@ -113,39 +53,13 @@ class QuickLogButtons extends ConsumerWidget {
|
||||
|
||||
Widget _buildQuickButton(
|
||||
BuildContext context, {
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required Color color,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
<<<<<<< HEAD
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, color: color, size: 24),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
=======
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
|
||||
return Expanded(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
@@ -157,7 +71,8 @@ class QuickLogButtons extends ConsumerWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(isDark ? 0.2 : 0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: isDark ? Border.all(color: color.withOpacity(0.3)) : null,
|
||||
border:
|
||||
isDark ? Border.all(color: color.withOpacity(0.3)) : null,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -174,7 +89,6 @@ class QuickLogButtons extends ConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -6,122 +6,45 @@ import '../models/cycle_entry.dart';
|
||||
class ScriptureCard extends StatelessWidget {
|
||||
final String verse;
|
||||
final String reference;
|
||||
<<<<<<< HEAD
|
||||
final CyclePhase phase;
|
||||
=======
|
||||
final String? translation;
|
||||
final CyclePhase phase;
|
||||
final VoidCallback? onTranslationTap;
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
|
||||
const ScriptureCard({
|
||||
super.key,
|
||||
required this.verse,
|
||||
required this.reference,
|
||||
<<<<<<< HEAD
|
||||
required this.phase,
|
||||
=======
|
||||
this.translation,
|
||||
required this.phase,
|
||||
this.onTranslationTap,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
<<<<<<< HEAD
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: _getGradientColors(phase),
|
||||
=======
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: _getGradientColors(context, phase),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
<<<<<<< HEAD
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _getPhaseColor(phase).withOpacity(0.2),
|
||||
=======
|
||||
border: Border.all(color: isDark ? Colors.white.withOpacity(0.05) : Colors.black.withOpacity(0.05)),
|
||||
border: Border.all(
|
||||
color: isDark
|
||||
? Colors.white.withOpacity(0.05)
|
||||
: Colors.black.withOpacity(0.05)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _getPhaseColor(phase).withOpacity(isDark ? 0.05 : 0.15),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
blurRadius: 15,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Scripture icon
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.menu_book_outlined,
|
||||
size: 18,
|
||||
color: AppColors.charcoal.withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Today\'s Verse',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.charcoal.withOpacity(0.7),
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Verse
|
||||
Text(
|
||||
'"$verse"',
|
||||
style: GoogleFonts.lora(
|
||||
fontSize: 16,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: AppColors.charcoal,
|
||||
height: 1.6,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Reference
|
||||
Text(
|
||||
'— $reference',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
],
|
||||
=======
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
@@ -136,13 +59,16 @@ class ScriptureCard extends StatelessWidget {
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: (isDark ? Colors.white : Colors.black).withOpacity(0.1),
|
||||
color: (isDark ? Colors.white : Colors.black)
|
||||
.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.menu_book_outlined,
|
||||
size: 18,
|
||||
color: isDark ? Colors.white70 : AppColors.charcoal.withOpacity(0.8),
|
||||
color: isDark
|
||||
? Colors.white70
|
||||
: AppColors.charcoal.withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -150,40 +76,46 @@ class ScriptureCard extends StatelessWidget {
|
||||
'Today\'s Verse',
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
fontSize: 12,
|
||||
color: isDark ? Colors.white60 : AppColors.charcoal.withOpacity(0.7),
|
||||
color: isDark
|
||||
? Colors.white60
|
||||
: AppColors.charcoal.withOpacity(0.7),
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
|
||||
// Verse
|
||||
Text(
|
||||
'"$verse"',
|
||||
style: scriptureStyle(context, fontSize: 17),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
|
||||
// Reference & Translation
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'— $reference',
|
||||
style: scriptureRefStyle(context).copyWith(fontSize: 13, fontWeight: FontWeight.w600),
|
||||
style: scriptureRefStyle(context)
|
||||
.copyWith(fontSize: 13, fontWeight: FontWeight.w600),
|
||||
),
|
||||
if (translation != null)
|
||||
InkWell(
|
||||
onTap: onTranslationTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: (isDark ? Colors.white : Colors.black).withOpacity(0.05),
|
||||
color: (isDark ? Colors.white : Colors.black)
|
||||
.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: (isDark ? Colors.white : Colors.black).withOpacity(0.1),
|
||||
color: (isDark ? Colors.white : Colors.black)
|
||||
.withOpacity(0.1),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
@@ -199,9 +131,11 @@ class ScriptureCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.swap_horiz,
|
||||
size: 14,
|
||||
color: isDark ? Colors.white38 : AppColors.warmGray,
|
||||
Icons.swap_horiz,
|
||||
size: 14,
|
||||
color: isDark
|
||||
? Colors.white38
|
||||
: AppColors.warmGray,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -212,38 +146,14 @@ class ScriptureCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
List<Color> _getGradientColors(CyclePhase phase) {
|
||||
switch (phase) {
|
||||
case CyclePhase.menstrual:
|
||||
return [
|
||||
AppColors.blushPink.withOpacity(0.6),
|
||||
AppColors.cream,
|
||||
];
|
||||
case CyclePhase.follicular:
|
||||
return [
|
||||
AppColors.sageGreen.withOpacity(0.3),
|
||||
AppColors.cream,
|
||||
];
|
||||
case CyclePhase.ovulation:
|
||||
return [
|
||||
AppColors.lavender.withOpacity(0.5),
|
||||
AppColors.cream,
|
||||
];
|
||||
case CyclePhase.luteal:
|
||||
return [
|
||||
AppColors.lutealPhase.withOpacity(0.3),
|
||||
AppColors.cream,
|
||||
=======
|
||||
List<Color> _getGradientColors(BuildContext context, CyclePhase phase) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final baseColor = isDark ? const Color(0xFF1E1E1E) : AppColors.cream;
|
||||
|
||||
|
||||
switch (phase) {
|
||||
case CyclePhase.menstrual:
|
||||
return [
|
||||
@@ -264,7 +174,6 @@ class ScriptureCard extends StatelessWidget {
|
||||
return [
|
||||
AppColors.lutealPhase.withOpacity(isDark ? 0.15 : 0.3),
|
||||
baseColor,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,22 @@ class TipCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
final tip = _getTipForPhase(phase, isMarried);
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
<<<<<<< HEAD
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.charcoal.withOpacity(0.05),
|
||||
=======
|
||||
color: theme.cardColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: isDark ? Border.all(color: Colors.white.withOpacity(0.05)) : null,
|
||||
border:
|
||||
isDark ? Border.all(color: Colors.white.withOpacity(0.05)) : null,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: (isDark ? Colors.black : AppColors.charcoal).withOpacity(0.05),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
color: (isDark ? Colors.black : AppColors.charcoal)
|
||||
.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
@@ -52,11 +43,7 @@ class TipCard extends StatelessWidget {
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
<<<<<<< HEAD
|
||||
color: AppColors.sageGreen.withOpacity(0.15),
|
||||
=======
|
||||
color: AppColors.sageGreen.withOpacity(isDark ? 0.2 : 0.15),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: const Icon(
|
||||
@@ -72,29 +59,14 @@ class TipCard extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
'Today\'s Tip',
|
||||
<<<<<<< HEAD
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
tip,
|
||||
<<<<<<< HEAD
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 13,
|
||||
color: AppColors.warmGray,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
=======
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 13,
|
||||
height: 1.4,
|
||||
@@ -122,7 +94,6 @@ class TipCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -131,21 +102,6 @@ class TipCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
String _getTipForPhase(CyclePhase phase, bool isMarried) {
|
||||
switch (phase) {
|
||||
case CyclePhase.menstrual:
|
||||
return 'This is a time for rest. Honor your body with extra sleep, warm drinks, and gentle movement. God designed your body with wisdom.';
|
||||
case CyclePhase.follicular:
|
||||
return 'Your energy is rising! This is a great time to start new projects, exercise more intensely, and spend time in community.';
|
||||
case CyclePhase.ovulation:
|
||||
if (isMarried) {
|
||||
return 'This is your most fertile window. You may feel more social and energetic. Prioritize connection with your spouse.';
|
||||
}
|
||||
return 'You may feel more social and confident during this phase. It\'s a great time for important conversations and presentations.';
|
||||
case CyclePhase.luteal:
|
||||
return 'As you enter the luteal phase, focus on nourishing foods, adequate sleep, and stress management. Be gentle with yourself.';
|
||||
=======
|
||||
void _showDetailedInsights(BuildContext context) {
|
||||
final details = _getDetailsForPhase(phase);
|
||||
showDialog(
|
||||
@@ -170,9 +126,11 @@ class TipCard extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildDetailSection('Nutrition', details['nutrition']!, Icons.restaurant),
|
||||
_buildDetailSection(
|
||||
'Nutrition', details['nutrition']!, Icons.restaurant),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailSection('Movement', details['movement']!, Icons.fitness_center),
|
||||
_buildDetailSection(
|
||||
'Movement', details['movement']!, Icons.fitness_center),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Note: While these are general trends, your body is unique. Always listen to your own energy levels.',
|
||||
@@ -187,7 +145,8 @@ class TipCard extends StatelessWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text('Got it', style: GoogleFonts.outfit(color: AppColors.sageGreen)),
|
||||
child: Text('Got it',
|
||||
style: GoogleFonts.outfit(color: AppColors.sageGreen)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -243,25 +202,32 @@ class TipCard extends StatelessWidget {
|
||||
switch (phase) {
|
||||
case CyclePhase.menstrual:
|
||||
return {
|
||||
'nutrition': 'Incorporate leafy greens, red meat or lentils for iron. Pair with citrus for better absorption.',
|
||||
'movement': 'Gentle walking, restorative yoga, or just deep breathing. Avoid high-intensity stress.',
|
||||
'nutrition':
|
||||
'Incorporate leafy greens, red meat or lentils for iron. Pair with citrus for better absorption.',
|
||||
'movement':
|
||||
'Gentle walking, restorative yoga, or just deep breathing. Avoid high-intensity stress.',
|
||||
};
|
||||
case CyclePhase.follicular:
|
||||
return {
|
||||
'nutrition': 'Broccoli, cauliflower, and fermented foods help balance rising estrogen. Focus on lean proteins.',
|
||||
'movement': 'Strength training and steady cardio. Your body is primed for building and renewal.',
|
||||
'nutrition':
|
||||
'Broccoli, cauliflower, and fermented foods help balance rising estrogen. Focus on lean proteins.',
|
||||
'movement':
|
||||
'Strength training and steady cardio. Your body is primed for building and renewal.',
|
||||
};
|
||||
case CyclePhase.ovulation:
|
||||
return {
|
||||
'nutrition': 'Avocados, nuts, and seeds provide healthy fats for peak hormonal health. Keep hydration high.',
|
||||
'movement': 'Highest intensity workouts, HIIT, or group sports. You have peak stamina and strength right now.',
|
||||
'nutrition':
|
||||
'Avocados, nuts, and seeds provide healthy fats for peak hormonal health. Keep hydration high.',
|
||||
'movement':
|
||||
'Highest intensity workouts, HIIT, or group sports. You have peak stamina and strength right now.',
|
||||
};
|
||||
case CyclePhase.luteal:
|
||||
return {
|
||||
'nutrition': 'Dark chocolate (70%+), pumpkin seeds, and bananas for magnesium to help with cramps.',
|
||||
'movement': 'Pilates, steady-state swimming, or hiking. Focus on persistence rather than peak intensity.',
|
||||
'nutrition':
|
||||
'Dark chocolate (70%+), pumpkin seeds, and bananas for magnesium to help with cramps.',
|
||||
'movement':
|
||||
'Pilates, steady-state swimming, or hiking. Focus on persistence rather than peak intensity.',
|
||||
};
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user