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:
@@ -2,55 +2,52 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../models/cycle_entry.dart';
|
||||
import '../../providers/navigation_provider.dart';
|
||||
import '../../providers/user_provider.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class LogScreen extends ConsumerStatefulWidget {
|
||||
<<<<<<< HEAD
|
||||
const LogScreen({super.key});
|
||||
=======
|
||||
final DateTime? initialDate;
|
||||
const LogScreen({super.key, this.initialDate});
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
|
||||
@override
|
||||
ConsumerState<LogScreen> createState() => _LogScreenState();
|
||||
}
|
||||
|
||||
class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
late DateTime _selectedDate;
|
||||
String? _existingEntryId;
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
bool _isPeriodDay = false;
|
||||
FlowIntensity? _flowIntensity;
|
||||
MoodLevel? _mood;
|
||||
int _energyLevel = 3;
|
||||
int? _energyLevel;
|
||||
int _crampIntensity = 0;
|
||||
bool _hasHeadache = false;
|
||||
bool _hasBloating = false;
|
||||
bool _hasBreastTenderness = false;
|
||||
bool _hasFatigue = false;
|
||||
bool _hasAcne = false;
|
||||
<<<<<<< HEAD
|
||||
final TextEditingController _notesController = TextEditingController();
|
||||
|
||||
@override
|
||||
=======
|
||||
bool _hasLowerBackPain = false;
|
||||
bool _hasConstipation = false;
|
||||
bool _hasDiarrhea = false;
|
||||
bool _hasInsomnia = false;
|
||||
int _stressLevel = 1;
|
||||
int? _stressLevel;
|
||||
final TextEditingController _notesController = TextEditingController();
|
||||
final TextEditingController _cravingsController = TextEditingController();
|
||||
|
||||
// Intimacy tracking
|
||||
bool _hadIntimacy = false;
|
||||
bool? _intimacyProtected; // null = no selection, true = protected, false = unprotected
|
||||
|
||||
// Hidden field to preserve husband's notes
|
||||
String? _husbandNotes;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedDate = widget.initialDate ?? DateTime.now();
|
||||
|
||||
|
||||
// Defer data loading to avoid build-time ref.read
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_loadExistingData();
|
||||
@@ -79,8 +76,12 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
_hasConstipation = entry.hasConstipation;
|
||||
_hasDiarrhea = entry.hasDiarrhea;
|
||||
_hasInsomnia = entry.hasInsomnia;
|
||||
_stressLevel = entry.stressLevel ?? 1;
|
||||
_stressLevel = entry.stressLevel;
|
||||
_notesController.text = entry.notes ?? '';
|
||||
_cravingsController.text = entry.cravings?.join(', ') ?? '';
|
||||
_husbandNotes = entry.husbandNotes;
|
||||
_hadIntimacy = entry.hadIntimacy;
|
||||
_intimacyProtected = entry.intimacyProtected;
|
||||
});
|
||||
} catch (_) {
|
||||
// No existing entry for this day
|
||||
@@ -88,21 +89,25 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
}
|
||||
|
||||
@override
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
void dispose() {
|
||||
_notesController.dispose();
|
||||
_cravingsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _saveEntry() async {
|
||||
List<String>? cravings;
|
||||
if (_cravingsController.text.isNotEmpty) {
|
||||
cravings = _cravingsController.text
|
||||
.split(',')
|
||||
.map((e) => e.trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
|
||||
final entry = CycleEntry(
|
||||
<<<<<<< HEAD
|
||||
id: const Uuid().v4(),
|
||||
date: DateTime.now(),
|
||||
=======
|
||||
id: _existingEntryId ?? const Uuid().v4(),
|
||||
date: _selectedDate,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
isPeriodDay: _isPeriodDay,
|
||||
flowIntensity: _isPeriodDay ? _flowIntensity : null,
|
||||
mood: _mood,
|
||||
@@ -113,60 +118,47 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
hasBreastTenderness: _hasBreastTenderness,
|
||||
hasFatigue: _hasFatigue,
|
||||
hasAcne: _hasAcne,
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
hasLowerBackPain: _hasLowerBackPain,
|
||||
hasConstipation: _hasConstipation,
|
||||
hasDiarrhea: _hasDiarrhea,
|
||||
hasInsomnia: _hasInsomnia,
|
||||
stressLevel: _stressLevel > 1 ? _stressLevel : null,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
stressLevel: _stressLevel,
|
||||
notes: _notesController.text.isNotEmpty ? _notesController.text : null,
|
||||
cravings: cravings,
|
||||
husbandNotes: _husbandNotes,
|
||||
hadIntimacy: _hadIntimacy,
|
||||
intimacyProtected: _hadIntimacy ? _intimacyProtected : null,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
<<<<<<< HEAD
|
||||
await ref.read(cycleEntriesProvider.notifier).addEntry(entry);
|
||||
=======
|
||||
if (_existingEntryId != null) {
|
||||
await ref.read(cycleEntriesProvider.notifier).updateEntry(entry);
|
||||
} else {
|
||||
await ref.read(cycleEntriesProvider.notifier).addEntry(entry);
|
||||
}
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Entry saved!', style: GoogleFonts.outfit()),
|
||||
<<<<<<< HEAD
|
||||
backgroundColor: AppColors.sageGreen,
|
||||
=======
|
||||
backgroundColor: AppColors.success,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
_resetForm();
|
||||
=======
|
||||
if (widget.initialDate != null) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
_resetForm();
|
||||
}
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
}
|
||||
}
|
||||
|
||||
void _resetForm() {
|
||||
setState(() {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
_existingEntryId = null;
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_isPeriodDay = false;
|
||||
_flowIntensity = null;
|
||||
_mood = null;
|
||||
@@ -177,26 +169,24 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
_hasBreastTenderness = false;
|
||||
_hasFatigue = false;
|
||||
_hasAcne = false;
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
_hasLowerBackPain = false;
|
||||
_hasConstipation = false;
|
||||
_hasDiarrhea = false;
|
||||
_hasInsomnia = false;
|
||||
_stressLevel = 1;
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_notesController.clear();
|
||||
_cravingsController.clear();
|
||||
_husbandNotes = null;
|
||||
_hadIntimacy = false;
|
||||
_intimacyProtected = null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -204,22 +194,6 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header
|
||||
<<<<<<< HEAD
|
||||
Text(
|
||||
'How are you feeling?',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_formatDate(DateTime.now()),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
=======
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -245,23 +219,21 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
),
|
||||
if (widget.initialDate == null)
|
||||
IconButton(
|
||||
onPressed: () => ref.read(navigationProvider.notifier).setIndex(0),
|
||||
onPressed: () =>
|
||||
ref.read(navigationProvider.notifier).setIndex(0),
|
||||
icon: const Icon(Icons.close),
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surfaceVariant.withOpacity(0.5),
|
||||
backgroundColor:
|
||||
theme.colorScheme.surfaceVariant.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Period Toggle
|
||||
_buildSectionCard(
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
context,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
title: 'Period',
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -270,11 +242,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
'Is today a period day?',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 16,
|
||||
<<<<<<< HEAD
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
color: theme.colorScheme.onSurface,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -291,10 +259,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
if (_isPeriodDay) ...[
|
||||
const SizedBox(height: 16),
|
||||
_buildSectionCard(
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
context,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
title: 'Flow Intensity',
|
||||
child: Row(
|
||||
children: FlowIntensity.values.map((flow) {
|
||||
@@ -302,31 +267,20 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _flowIntensity = flow),
|
||||
<<<<<<< HEAD
|
||||
child: Container(
|
||||
=======
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
<<<<<<< HEAD
|
||||
? AppColors.menstrualPhase.withOpacity(0.2)
|
||||
: AppColors.lightGray.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: isSelected
|
||||
? Border.all(color: AppColors.menstrualPhase)
|
||||
: null,
|
||||
=======
|
||||
? AppColors.menstrualPhase.withOpacity(isDark ? 0.3 : 0.2)
|
||||
: theme.colorScheme.surfaceVariant.withOpacity(0.3),
|
||||
? AppColors.menstrualPhase
|
||||
.withOpacity(isDark ? 0.3 : 0.2)
|
||||
: theme.colorScheme.surfaceVariant
|
||||
.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: isSelected
|
||||
? Border.all(color: AppColors.menstrualPhase)
|
||||
: Border.all(color: Colors.transparent),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -334,11 +288,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
Icons.water_drop,
|
||||
color: isSelected
|
||||
? AppColors.menstrualPhase
|
||||
<<<<<<< HEAD
|
||||
: AppColors.warmGray,
|
||||
=======
|
||||
: theme.colorScheme.onSurfaceVariant,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -346,16 +296,12 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
flow.label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 11,
|
||||
<<<<<<< HEAD
|
||||
color: isSelected
|
||||
? AppColors.menstrualPhase
|
||||
: AppColors.warmGray,
|
||||
=======
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
|
||||
fontWeight: isSelected
|
||||
? FontWeight.w600
|
||||
: FontWeight.w400,
|
||||
color: isSelected
|
||||
? AppColors.menstrualPhase
|
||||
: theme.colorScheme.onSurfaceVariant,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -372,10 +318,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
|
||||
// Mood
|
||||
_buildSectionCard(
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
context,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
title: 'Mood',
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
@@ -383,29 +326,18 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
final isSelected = _mood == mood;
|
||||
return GestureDetector(
|
||||
onTap: () => setState(() => _mood = mood),
|
||||
<<<<<<< HEAD
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? AppColors.softGold.withOpacity(0.2)
|
||||
=======
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? AppColors.softGold.withOpacity(isDark ? 0.3 : 0.2)
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
? AppColors.softGold
|
||||
.withOpacity(isDark ? 0.3 : 0.2)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: isSelected
|
||||
? Border.all(color: AppColors.softGold)
|
||||
<<<<<<< HEAD
|
||||
: null,
|
||||
=======
|
||||
: Border.all(color: Colors.transparent),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -420,16 +352,12 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
mood.label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 10,
|
||||
<<<<<<< HEAD
|
||||
color: isSelected
|
||||
? AppColors.softGold
|
||||
: AppColors.warmGray,
|
||||
=======
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
|
||||
fontWeight: isSelected
|
||||
? FontWeight.w600
|
||||
: FontWeight.w400,
|
||||
color: isSelected
|
||||
? AppColors.softGold
|
||||
: theme.colorScheme.onSurfaceVariant,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -442,16 +370,6 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Energy Level
|
||||
_buildSectionCard(
|
||||
title: 'Energy Level',
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.battery_1_bar, color: AppColors.warmGray),
|
||||
=======
|
||||
// Energy & Stress Levels
|
||||
_buildSectionCard(
|
||||
context,
|
||||
@@ -471,33 +389,18 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: _energyLevel.toDouble(),
|
||||
value: (_energyLevel ?? 3).toDouble(),
|
||||
min: 1,
|
||||
max: 5,
|
||||
divisions: 4,
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
activeColor: AppColors.sageGreen,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
onChanged: (value) {
|
||||
setState(() => _energyLevel = value.round());
|
||||
},
|
||||
),
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
const Icon(Icons.battery_full, color: AppColors.sageGreen),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
_getEnergyLabel(_energyLevel),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 13,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
=======
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: Text(
|
||||
@@ -527,7 +430,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: _stressLevel.toDouble(),
|
||||
value: (_stressLevel ?? 1).toDouble(),
|
||||
min: 1,
|
||||
max: 5,
|
||||
divisions: 4,
|
||||
@@ -540,7 +443,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: Text(
|
||||
'$_stressLevel/5',
|
||||
'${_stressLevel ?? 1}/5',
|
||||
textAlign: TextAlign.end,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
@@ -549,7 +452,6 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
),
|
||||
),
|
||||
],
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -559,10 +461,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
|
||||
// Symptoms
|
||||
_buildSectionCard(
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
context,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
title: 'Symptoms',
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -575,11 +474,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
'Cramps',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
<<<<<<< HEAD
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
color: theme.colorScheme.onSurface,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -596,22 +491,15 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
<<<<<<< HEAD
|
||||
width: 40,
|
||||
child: Text(
|
||||
_crampIntensity == 0 ? 'None' : '$_crampIntensity/5',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
color: AppColors.warmGray,
|
||||
=======
|
||||
width: 50,
|
||||
child: Text(
|
||||
_crampIntensity == 0 ? 'None' : '$_crampIntensity/5',
|
||||
_crampIntensity == 0
|
||||
? 'None'
|
||||
: '$_crampIntensity/5',
|
||||
textAlign: TextAlign.end,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -623,23 +511,29 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
<<<<<<< HEAD
|
||||
_buildSymptomChip('Headache', _hasHeadache, (v) => setState(() => _hasHeadache = v)),
|
||||
_buildSymptomChip('Bloating', _hasBloating, (v) => setState(() => _hasBloating = v)),
|
||||
_buildSymptomChip('Breast Tenderness', _hasBreastTenderness, (v) => setState(() => _hasBreastTenderness = v)),
|
||||
_buildSymptomChip('Fatigue', _hasFatigue, (v) => setState(() => _hasFatigue = v)),
|
||||
_buildSymptomChip('Acne', _hasAcne, (v) => setState(() => _hasAcne = v)),
|
||||
=======
|
||||
_buildSymptomChip(context, 'Headache', _hasHeadache, (v) => setState(() => _hasHeadache = v)),
|
||||
_buildSymptomChip(context, 'Bloating', _hasBloating, (v) => setState(() => _hasBloating = v)),
|
||||
_buildSymptomChip(context, 'Breast Tenderness', _hasBreastTenderness, (v) => setState(() => _hasBreastTenderness = v)),
|
||||
_buildSymptomChip(context, 'Fatigue', _hasFatigue, (v) => setState(() => _hasFatigue = v)),
|
||||
_buildSymptomChip(context, 'Acne', _hasAcne, (v) => setState(() => _hasAcne = v)),
|
||||
_buildSymptomChip(context, 'Back Pain', _hasLowerBackPain, (v) => setState(() => _hasLowerBackPain = v)),
|
||||
_buildSymptomChip(context, 'Constipation', _hasConstipation, (v) => setState(() => _hasConstipation = v)),
|
||||
_buildSymptomChip(context, 'Diarrhea', _hasDiarrhea, (v) => setState(() => _hasDiarrhea = v)),
|
||||
_buildSymptomChip(context, 'Insomnia', _hasInsomnia, (v) => setState(() => _hasInsomnia = v)),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_buildSymptomChip(context, 'Headache', _hasHeadache,
|
||||
(v) => setState(() => _hasHeadache = v)),
|
||||
_buildSymptomChip(context, 'Bloating', _hasBloating,
|
||||
(v) => setState(() => _hasBloating = v)),
|
||||
_buildSymptomChip(context, 'Breast Tenderness',
|
||||
_hasBreastTenderness,
|
||||
(v) => setState(() => _hasBreastTenderness = v)),
|
||||
_buildSymptomChip(context, 'Fatigue', _hasFatigue,
|
||||
(v) => setState(() => _hasFatigue = v)),
|
||||
_buildSymptomChip(context, 'Acne', _hasAcne,
|
||||
(v) => setState(() => _hasAcne = v)),
|
||||
_buildSymptomChip(context, 'Back Pain',
|
||||
_hasLowerBackPain,
|
||||
(v) => setState(() => _hasLowerBackPain = v)),
|
||||
_buildSymptomChip(
|
||||
context,
|
||||
'Constipation',
|
||||
_hasConstipation,
|
||||
(v) => setState(() => _hasConstipation = v)),
|
||||
_buildSymptomChip(context, 'Diarrhea', _hasDiarrhea,
|
||||
(v) => setState(() => _hasDiarrhea = v)),
|
||||
_buildSymptomChip(context, 'Insomnia', _hasInsomnia,
|
||||
(v) => setState(() => _hasInsomnia = v)),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -648,31 +542,138 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Notes
|
||||
// Cravings
|
||||
_buildSectionCard(
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
context,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
title: 'Notes',
|
||||
title: 'Cravings',
|
||||
child: TextField(
|
||||
controller: _notesController,
|
||||
maxLines: 3,
|
||||
controller: _cravingsController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Add any notes about how you\'re feeling...',
|
||||
<<<<<<< HEAD
|
||||
hintStyle: GoogleFonts.outfit(
|
||||
color: AppColors.lightGray,
|
||||
fontSize: 14,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
hintText: 'e.g., Chocolate, salty chips (comma separated)',
|
||||
filled: true,
|
||||
fillColor: isDark ? theme.colorScheme.surface : theme.colorScheme.surfaceVariant.withOpacity(0.1),
|
||||
fillColor: isDark
|
||||
? theme.colorScheme.surface
|
||||
: theme.colorScheme.surfaceVariant.withOpacity(0.1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Intimacy Tracking (for married users)
|
||||
_buildSectionCard(
|
||||
context,
|
||||
title: 'Intimacy',
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: Text('Had Intimacy Today', style: GoogleFonts.outfit(fontSize: 14)),
|
||||
value: _hadIntimacy,
|
||||
onChanged: (val) => setState(() {
|
||||
_hadIntimacy = val;
|
||||
if (!val) _intimacyProtected = null;
|
||||
}),
|
||||
activeColor: AppColors.sageGreen,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
if (_hadIntimacy) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text('Protection:', style: GoogleFonts.outfit(fontSize: 13, color: AppColors.warmGray)),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _intimacyProtected = true),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: _intimacyProtected == true
|
||||
? AppColors.sageGreen.withOpacity(0.2)
|
||||
: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: _intimacyProtected == true
|
||||
? AppColors.sageGreen
|
||||
: Colors.grey.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Protected',
|
||||
style: GoogleFonts.outfit(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _intimacyProtected == true
|
||||
? AppColors.sageGreen
|
||||
: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _intimacyProtected = false),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: _intimacyProtected == false
|
||||
? AppColors.rose.withOpacity(0.15)
|
||||
: Colors.grey.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: _intimacyProtected == false
|
||||
? AppColors.rose
|
||||
: Colors.grey.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Unprotected',
|
||||
style: GoogleFonts.outfit(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _intimacyProtected == false
|
||||
? AppColors.rose
|
||||
: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Notes
|
||||
_buildSectionCard(
|
||||
context,
|
||||
title: 'Notes',
|
||||
child: TextField(
|
||||
controller: _notesController,
|
||||
maxLines: 3,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Add any notes about how you\'re feeling...',
|
||||
filled: true,
|
||||
fillColor: isDark
|
||||
? theme.colorScheme.surface
|
||||
: theme.colorScheme.surfaceVariant.withOpacity(0.1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
@@ -681,7 +682,6 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.onSurface,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -691,10 +691,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
// Save Button
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
height: 54,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveEntry,
|
||||
child: const Text('Save Entry'),
|
||||
@@ -707,36 +704,27 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
Widget _buildSectionCard({required String title, required Widget child}) {
|
||||
=======
|
||||
Widget _buildSectionCard(BuildContext context, {required String title, required Widget child}) {
|
||||
Widget _buildSectionCard(BuildContext context,
|
||||
{required String title, required Widget child}) {
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
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.cardTheme.color,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.colorScheme.outline.withOpacity(0.05)),
|
||||
boxShadow: isDark ? null : [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
boxShadow: isDark
|
||||
? null
|
||||
: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -746,11 +734,7 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
<<<<<<< HEAD
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
color: theme.colorScheme.onSurface,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -760,25 +744,8 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
Widget _buildSymptomChip(String label, bool isSelected, ValueChanged<bool> onChanged) {
|
||||
return GestureDetector(
|
||||
onTap: () => onChanged(!isSelected),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppColors.lavender.withOpacity(0.3) : AppColors.lightGray.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: isSelected ? Border.all(color: AppColors.lavender) : null,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 13,
|
||||
color: isSelected ? AppColors.ovulationPhase : AppColors.warmGray,
|
||||
fontWeight: isSelected ? FontWeight.w500 : FontWeight.w400,
|
||||
=======
|
||||
Widget _buildSymptomChip(BuildContext context, String label, bool isSelected, ValueChanged<bool> onChanged) {
|
||||
Widget _buildSymptomChip(BuildContext context, String label, bool isSelected,
|
||||
ValueChanged<bool> onChanged) {
|
||||
final theme = Theme.of(context);
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
|
||||
@@ -791,22 +758,23 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? theme.colorScheme.tertiary.withOpacity(isDark ? 0.3 : 0.2)
|
||||
color: isSelected
|
||||
? theme.colorScheme.tertiary.withOpacity(isDark ? 0.3 : 0.2)
|
||||
: theme.colorScheme.surfaceVariant.withOpacity(0.3),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: isSelected
|
||||
? Border.all(color: theme.colorScheme.tertiary)
|
||||
border: isSelected
|
||||
? Border.all(color: theme.colorScheme.tertiary)
|
||||
: Border.all(color: Colors.transparent),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 13,
|
||||
color: isSelected ? theme.colorScheme.onSurface : theme.colorScheme.onSurfaceVariant,
|
||||
color: isSelected
|
||||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.onSurfaceVariant,
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400,
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -814,31 +782,42 @@ class _LogScreenState extends ConsumerState<LogScreen> {
|
||||
}
|
||||
|
||||
String _formatDate(DateTime date) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
final now = DateTime.now();
|
||||
if (DateUtils.isSameDay(date, now)) {
|
||||
return 'Today, ${_getMonth(date.month)} ${date.day}';
|
||||
}
|
||||
const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||
const days = [
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
'Sunday'
|
||||
];
|
||||
return '${days[date.weekday - 1]}, ${_getMonth(date.month)} ${date.day}';
|
||||
}
|
||||
|
||||
String _getMonth(int month) {
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
const months = [
|
||||
'January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
];
|
||||
<<<<<<< HEAD
|
||||
const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||
return '${days[date.weekday - 1]}, ${months[date.month - 1]} ${date.day}';
|
||||
=======
|
||||
return months[month - 1];
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
}
|
||||
|
||||
String _getEnergyLabel(int level) {
|
||||
String _getEnergyLabel(int? level) {
|
||||
if (level == null) return 'Not logged';
|
||||
switch (level) {
|
||||
case 1:
|
||||
return 'Very Low';
|
||||
|
||||
Reference in New Issue
Block a user