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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user