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:
@@ -1,3 +1,4 @@
|
||||
import 'package:christian_period_tracker/models/scripture.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
@@ -7,13 +8,15 @@ import '../../models/cycle_entry.dart';
|
||||
import '../../providers/user_provider.dart';
|
||||
import '../../services/cycle_service.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
import '../log/log_screen.dart';
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
|
||||
class CalendarScreen extends ConsumerStatefulWidget {
|
||||
const CalendarScreen({super.key});
|
||||
final bool readOnly;
|
||||
|
||||
const CalendarScreen({
|
||||
super.key,
|
||||
this.readOnly = false,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<CalendarScreen> createState() => _CalendarScreenState();
|
||||
@@ -33,7 +36,7 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
children: [
|
||||
// Header
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -145,46 +148,12 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
),
|
||||
),
|
||||
calendarBuilders: CalendarBuilders(
|
||||
<<<<<<< HEAD
|
||||
markerBuilder: (context, date, events) {
|
||||
// Check if it's a logged period day
|
||||
final isLoggedPeriod = _isLoggedPeriodDay(date, entries);
|
||||
|
||||
if (isLoggedPeriod) {
|
||||
return Positioned(
|
||||
bottom: 1,
|
||||
child: Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.menstrualPhase,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final phase = _getPhaseForDate(date, lastPeriodStart, cycleLength);
|
||||
if (phase != null) {
|
||||
return Positioned(
|
||||
bottom: 1,
|
||||
child: Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(phase).withOpacity(0.5),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
=======
|
||||
markerBuilder: (context, date, entries) {
|
||||
final entry = _getEntryForDate(date, entries);
|
||||
|
||||
|
||||
if (entry == null) {
|
||||
final phase = _getPhaseForDate(date, lastPeriodStart, cycleLength);
|
||||
final phase =
|
||||
_getPhaseForDate(date, lastPeriodStart, cycleLength);
|
||||
if (phase != null) {
|
||||
return Positioned(
|
||||
bottom: 1,
|
||||
@@ -200,7 +169,7 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// If we have an entry, show icons/markers
|
||||
return Positioned(
|
||||
bottom: 1,
|
||||
@@ -217,7 +186,9 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
if (entry.mood != null || entry.energyLevel != 3 || entry.hasSymptoms)
|
||||
if (entry.mood != null ||
|
||||
entry.energyLevel != 3 ||
|
||||
entry.hasSymptoms)
|
||||
Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
@@ -230,7 +201,6 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
],
|
||||
),
|
||||
);
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -241,7 +211,8 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
// Selected Day Info
|
||||
if (_selectedDay != null)
|
||||
Expanded(
|
||||
child: _buildDayInfo(_selectedDay!, lastPeriodStart, cycleLength, entries),
|
||||
child: _buildDayInfo(
|
||||
_selectedDay!, lastPeriodStart, cycleLength, entries),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -333,20 +304,11 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDayInfo(DateTime date, DateTime? lastPeriodStart, int cycleLength, List<CycleEntry> entries) {
|
||||
Widget _buildDayInfo(DateTime date, DateTime? lastPeriodStart, int cycleLength,
|
||||
List<CycleEntry> entries) {
|
||||
final phase = _getPhaseForDate(date, lastPeriodStart, cycleLength);
|
||||
final entry = _getEntryForDate(date, entries);
|
||||
<<<<<<< HEAD
|
||||
final isLoggedPeriod = entry?.isPeriodDay ?? false;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
=======
|
||||
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -360,94 +322,31 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
<<<<<<< HEAD
|
||||
Text(
|
||||
'${_getMonthName(date.month)} ${date.day}, ${date.year}',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (phase != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(phase).withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(phase.emoji),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
phase.label,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _getPhaseColor(phase),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (isLoggedPeriod)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.menstrualPhase.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.menstrualPhase.withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.water_drop, color: AppColors.menstrualPhase, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Period Recorded',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.menstrualPhase,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
phase?.description ?? 'No cycle data for this date',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
=======
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${_getMonthName(date.month)} ${date.day}, ${date.year}',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
if (entry != null)
|
||||
const Icon(Icons.check_circle, color: AppColors.sageGreen, size: 20),
|
||||
const Icon(Icons.check_circle,
|
||||
color: AppColors.sageGreen, size: 20),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
if (phase != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(phase).withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -469,34 +368,38 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (entry == null)
|
||||
Text(
|
||||
phase?.description ?? 'No data for this date',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: AppColors.warmGray),
|
||||
)
|
||||
else ...[
|
||||
// Period Detail
|
||||
if (entry.isPeriodDay)
|
||||
_buildDetailRow(Icons.water_drop, 'Period Day', AppColors.menstrualPhase,
|
||||
_buildDetailRow(Icons.water_drop, 'Period Day',
|
||||
AppColors.menstrualPhase,
|
||||
value: entry.flowIntensity?.label),
|
||||
|
||||
|
||||
// Mood Detail
|
||||
if (entry.mood != null)
|
||||
_buildDetailRow(Icons.emoji_emotions_outlined, 'Mood', AppColors.softGold,
|
||||
_buildDetailRow(
|
||||
Icons.emoji_emotions_outlined, 'Mood', AppColors.softGold,
|
||||
value: '${entry.mood!.emoji} ${entry.mood!.label}'),
|
||||
|
||||
|
||||
// Energy Detail
|
||||
_buildDetailRow(Icons.flash_on, 'Energy Level', AppColors.follicularPhase,
|
||||
_buildDetailRow(
|
||||
Icons.flash_on, 'Energy Level', AppColors.follicularPhase,
|
||||
value: _getEnergyLabel(entry.energyLevel)),
|
||||
|
||||
|
||||
// Symptoms
|
||||
if (entry.hasSymptoms)
|
||||
_buildDetailRow(Icons.healing_outlined, 'Symptoms', AppColors.lavender,
|
||||
_buildDetailRow(
|
||||
Icons.healing_outlined, 'Symptoms', AppColors.lavender,
|
||||
value: _getSymptomsString(entry)),
|
||||
|
||||
|
||||
// Contextual Recommendation
|
||||
_buildRecommendation(entry),
|
||||
|
||||
@@ -507,40 +410,49 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Notes', style: GoogleFonts.outfit(fontSize: 12, fontWeight: FontWeight.w600, color: AppColors.warmGray)),
|
||||
Text('Notes',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.warmGray)),
|
||||
const SizedBox(height: 4),
|
||||
Text(entry.notes!, style: GoogleFonts.outfit(fontSize: 14)),
|
||||
Text(entry.notes!,
|
||||
style: GoogleFonts.outfit(fontSize: 14)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
|
||||
// Action Buttons
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Log for ${_getMonthName(date.month)} ${date.day}'),
|
||||
if (!widget.readOnly)
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'Log for ${_getMonthName(date.month)} ${date.day}'),
|
||||
),
|
||||
body: LogScreen(initialDate: date),
|
||||
),
|
||||
body: LogScreen(initialDate: date),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: Icon(entry != null ? Icons.edit_note : Icons.add_circle_outline),
|
||||
label: Text(entry != null ? 'Edit Log' : 'Add Log'),
|
||||
);
|
||||
},
|
||||
icon: Icon(entry != null
|
||||
? Icons.edit_note
|
||||
: Icons.add_circle_outline),
|
||||
label: Text(entry != null ? 'Edit Log' : 'Add Log'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.sageGreen,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -552,7 +464,7 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
}
|
||||
|
||||
Widget _buildRecommendation(CycleEntry entry) {
|
||||
final scripture = ScriptureDatabase.getRecommendedScripture(entry);
|
||||
final scripture = ScriptureDatabase().getRecommendedScripture(entry);
|
||||
if (scripture == null) return const SizedBox.shrink();
|
||||
|
||||
final user = ref.read(userProfileProvider);
|
||||
@@ -572,7 +484,8 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.auto_awesome, color: AppColors.softGold, size: 18),
|
||||
const Icon(Icons.auto_awesome,
|
||||
color: AppColors.softGold, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Daily Encouragement',
|
||||
@@ -600,7 +513,6 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
@@ -609,9 +521,8 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
Widget _buildDetailRow(IconData icon, String label, Color color, {String? value}) {
|
||||
Widget _buildDetailRow(IconData icon, String label, Color color,
|
||||
{String? value}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
@@ -652,7 +563,8 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
|
||||
String _getSymptomsString(CycleEntry entry) {
|
||||
List<String> s = [];
|
||||
if (entry.crampIntensity != null && entry.crampIntensity! > 0) s.add('Cramps (${entry.crampIntensity}/5)');
|
||||
if (entry.crampIntensity != null && entry.crampIntensity! > 0)
|
||||
s.add('Cramps (${entry.crampIntensity}/5)');
|
||||
if (entry.hasHeadache) s.add('Headache');
|
||||
if (entry.hasBloating) s.add('Bloating');
|
||||
if (entry.hasBreastTenderness) s.add('Breast Tenderness');
|
||||
@@ -661,15 +573,24 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
return s.join(', ');
|
||||
}
|
||||
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
CyclePhase? _getPhaseForDate(DateTime date, DateTime? lastPeriodStart, int cycleLength) {
|
||||
String _getEnergyLabel(int? energyLevel) {
|
||||
if (energyLevel == null) return 'Not logged';
|
||||
if (energyLevel <= 1) return 'Very Low';
|
||||
if (energyLevel == 2) return 'Low';
|
||||
if (energyLevel == 3) return 'Neutral';
|
||||
if (energyLevel == 4) return 'High';
|
||||
return 'Very High';
|
||||
}
|
||||
|
||||
CyclePhase? _getPhaseForDate(
|
||||
DateTime date, DateTime? lastPeriodStart, int cycleLength) {
|
||||
if (lastPeriodStart == null) return null;
|
||||
|
||||
|
||||
final daysSinceLastPeriod = date.difference(lastPeriodStart).inDays;
|
||||
if (daysSinceLastPeriod < 0) return null;
|
||||
|
||||
|
||||
final dayOfCycle = (daysSinceLastPeriod % cycleLength) + 1;
|
||||
|
||||
|
||||
if (dayOfCycle <= 5) return CyclePhase.menstrual;
|
||||
if (dayOfCycle <= 13) return CyclePhase.follicular;
|
||||
if (dayOfCycle <= 16) return CyclePhase.ovulation;
|
||||
@@ -691,8 +612,18 @@ class _CalendarScreenState extends ConsumerState<CalendarScreen> {
|
||||
|
||||
String _getMonthName(int month) {
|
||||
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'
|
||||
];
|
||||
return months[month - 1];
|
||||
}
|
||||
|
||||
@@ -6,18 +6,31 @@ import '../../providers/user_provider.dart';
|
||||
import '../../services/cycle_service.dart';
|
||||
import '../../models/cycle_entry.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
import '../../widgets/scripture_card.dart';
|
||||
import '../../models/user_profile.dart';
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
import '../../providers/scripture_provider.dart'; // Import the new provider
|
||||
|
||||
class DevotionalScreen extends ConsumerWidget {
|
||||
class DevotionalScreen extends ConsumerStatefulWidget {
|
||||
const DevotionalScreen({super.key});
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
Future<void> _showTranslationPicker(BuildContext context, WidgetRef ref, UserProfile? user) async {
|
||||
@override
|
||||
ConsumerState<DevotionalScreen> createState() => _DevotionalScreenState();
|
||||
}
|
||||
|
||||
class _DevotionalScreenState extends ConsumerState<DevotionalScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeScripture();
|
||||
}
|
||||
|
||||
Future<void> _initializeScripture() async {
|
||||
final phase = ref.read(currentCycleInfoProvider).phase;
|
||||
await ref.read(scriptureProvider.notifier).initializeScripture(phase);
|
||||
}
|
||||
|
||||
Future<void> _showTranslationPicker(
|
||||
BuildContext context, WidgetRef ref, UserProfile? user) async {
|
||||
if (user == null) return;
|
||||
|
||||
final selected = await showModalBottomSheet<BibleTranslation>(
|
||||
@@ -38,32 +51,46 @@ class DevotionalScreen extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
...BibleTranslation.values.map((t) => ListTile(
|
||||
title: Text(t.label),
|
||||
trailing: user.bibleTranslation == t
|
||||
? Icon(Icons.check, color: AppColors.sageGreen)
|
||||
: null,
|
||||
onTap: () => Navigator.pop(context, t),
|
||||
)),
|
||||
title: Text(t.label),
|
||||
trailing: user.bibleTranslation == t
|
||||
? Icon(Icons.check, color: AppColors.sageGreen)
|
||||
: null,
|
||||
onTap: () => Navigator.pop(context, t),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (selected != null) {
|
||||
await ref.read(userProfileProvider.notifier).updateProfile(
|
||||
user.copyWith(bibleTranslation: selected)
|
||||
);
|
||||
await ref
|
||||
.read(userProfileProvider.notifier)
|
||||
.updateProfile(user.copyWith(bibleTranslation: selected));
|
||||
}
|
||||
}
|
||||
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Widget build(BuildContext context) {
|
||||
// Listen for changes in the cycle info to re-initialize scripture if needed
|
||||
ref.listen<CycleInfo>(currentCycleInfoProvider, (previousCycleInfo, newCycleInfo) {
|
||||
if (previousCycleInfo?.phase != newCycleInfo.phase) {
|
||||
_initializeScripture();
|
||||
}
|
||||
});
|
||||
|
||||
final user = ref.watch(userProfileProvider);
|
||||
final cycleInfo = ref.watch(currentCycleInfoProvider);
|
||||
|
||||
final phase = cycleInfo['phase'] as CyclePhase;
|
||||
final scripture = ScriptureDatabase.getScriptureForPhase(phase.name);
|
||||
|
||||
final phase = cycleInfo.phase;
|
||||
|
||||
// Watch the scripture provider for the current scripture
|
||||
final scriptureState = ref.watch(scriptureProvider);
|
||||
final scripture = scriptureState.currentScripture;
|
||||
final maxIndex = scriptureState.maxIndex;
|
||||
|
||||
if (scripture == null) {
|
||||
return const Center(child: CircularProgressIndicator()); // Or some error message
|
||||
}
|
||||
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
@@ -85,7 +112,8 @@ class DevotionalScreen extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(phase).withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -117,75 +145,53 @@ class DevotionalScreen extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Main Scripture Card
|
||||
<<<<<<< HEAD
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
_getPhaseColor(phase).withOpacity(0.15),
|
||||
AppColors.cream,
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
// Main Scripture Card with Navigation
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
ScriptureCard(
|
||||
verse: scripture
|
||||
.getVerse(user?.bibleTranslation ?? BibleTranslation.esv),
|
||||
reference: scripture.reference,
|
||||
translation:
|
||||
(user?.bibleTranslation ?? BibleTranslation.esv).label,
|
||||
phase: phase,
|
||||
onTranslationTap: () =>
|
||||
_showTranslationPicker(context, ref, user),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
border: Border.all(
|
||||
color: _getPhaseColor(phase).withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Quote icon
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: _getPhaseColor(phase).withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.format_quote,
|
||||
color: _getPhaseColor(phase),
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Verse
|
||||
Text(
|
||||
'"${scripture.verse}"',
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.lora(
|
||||
fontSize: 20,
|
||||
fontStyle: FontStyle.italic,
|
||||
if (maxIndex != null && maxIndex > 1) ...[
|
||||
Positioned(
|
||||
left: 0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getPreviousScripture(),
|
||||
color: AppColors.charcoal,
|
||||
height: 1.6,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Reference
|
||||
Text(
|
||||
'— ${scripture.reference}',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.warmGray,
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.arrow_forward_ios),
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getNextScripture(),
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (maxIndex != null && maxIndex > 1)
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () => ref.read(scriptureProvider.notifier).getRandomScripture(),
|
||||
icon: const Icon(Icons.shuffle),
|
||||
label: const Text('Random Verse'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.sageGreen,
|
||||
),
|
||||
),
|
||||
=======
|
||||
ScriptureCard(
|
||||
verse: scripture.getVerse(user?.bibleTranslation ?? BibleTranslation.esv),
|
||||
reference: scripture.reference,
|
||||
translation: (user?.bibleTranslation ?? BibleTranslation.esv).label,
|
||||
phase: phase,
|
||||
onTranslationTap: () => _showTranslationPicker(context, ref, user),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
<<<<<<< HEAD
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
=======
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
import '../../theme/app_theme.dart';
|
||||
import '../../models/user_profile.dart';
|
||||
import '../../models/cycle_entry.dart';
|
||||
@@ -17,28 +13,10 @@ import '../../widgets/cycle_ring.dart';
|
||||
import '../../widgets/scripture_card.dart';
|
||||
import '../../widgets/quick_log_buttons.dart';
|
||||
import '../../providers/user_provider.dart';
|
||||
<<<<<<< HEAD
|
||||
import '../../services/cycle_service.dart';
|
||||
|
||||
class HomeScreen extends ConsumerStatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<HomeScreen> createState() => _HomeScreenState();
|
||||
}
|
||||
|
||||
class _HomeScreenState extends ConsumerState<HomeScreen> {
|
||||
int _selectedIndex = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: IndexedStack(
|
||||
index: _selectedIndex,
|
||||
=======
|
||||
import '../../providers/navigation_provider.dart';
|
||||
import '../../services/cycle_service.dart';
|
||||
import '../../services/bible_utils.dart';
|
||||
import '../../providers/scripture_provider.dart'; // Import the new provider
|
||||
|
||||
class HomeScreen extends ConsumerWidget {
|
||||
const HomeScreen({super.key});
|
||||
@@ -50,45 +28,34 @@ class HomeScreen extends ConsumerWidget {
|
||||
return Scaffold(
|
||||
body: IndexedStack(
|
||||
index: selectedIndex,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
children: [
|
||||
const _DashboardTab(),
|
||||
const CalendarScreen(),
|
||||
const LogScreen(),
|
||||
const DevotionalScreen(),
|
||||
<<<<<<< HEAD
|
||||
_SettingsTab(onReset: () => setState(() => _selectedIndex = 0)),
|
||||
=======
|
||||
_SettingsTab(onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_SettingsTab(
|
||||
onReset: () =>
|
||||
ref.read(navigationProvider.notifier).setIndex(0)),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: Container(
|
||||
decoration: BoxDecoration(
|
||||
<<<<<<< HEAD
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.charcoal.withOpacity(0.1),
|
||||
=======
|
||||
color: Theme.of(context).bottomNavigationBarTheme.backgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: (Theme.of(context).brightness == Brightness.dark ? Colors.black : AppColors.charcoal).withOpacity(0.1),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
color: (Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.black
|
||||
: AppColors.charcoal)
|
||||
.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: BottomNavigationBar(
|
||||
<<<<<<< HEAD
|
||||
currentIndex: _selectedIndex,
|
||||
onTap: (index) => setState(() => _selectedIndex = index),
|
||||
=======
|
||||
currentIndex: selectedIndex,
|
||||
onTap: (index) => ref.read(navigationProvider.notifier).setIndex(index),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
onTap: (index) =>
|
||||
ref.read(navigationProvider.notifier).setIndex(index),
|
||||
items: const [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
@@ -122,34 +89,61 @@ class HomeScreen extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _DashboardTab extends ConsumerWidget {
|
||||
const _DashboardTab();
|
||||
class _DashboardTab extends ConsumerStatefulWidget {
|
||||
const _DashboardTab({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
<<<<<<< HEAD
|
||||
final user = ref.watch(userProfileProvider);
|
||||
ConsumerState<_DashboardTab> createState() => _DashboardTabState();
|
||||
}
|
||||
|
||||
class _DashboardTabState extends ConsumerState<_DashboardTab> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeScripture();
|
||||
}
|
||||
|
||||
// This method initializes the scripture and can react to phase changes.
|
||||
// It's called from initState and also when currentCycleInfoProvider changes.
|
||||
Future<void> _initializeScripture() async {
|
||||
final phase = ref.read(currentCycleInfoProvider).phase;
|
||||
await ref.read(scriptureProvider.notifier).initializeScripture(phase);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Listen for changes in the cycle info to re-initialize scripture if needed
|
||||
ref.listen<CycleInfo>(currentCycleInfoProvider, (previousCycleInfo, newCycleInfo) {
|
||||
if (previousCycleInfo?.phase != newCycleInfo.phase) {
|
||||
_initializeScripture();
|
||||
}
|
||||
});
|
||||
|
||||
final name =
|
||||
ref.watch(userProfileProvider.select((u) => u?.name)) ?? 'Friend';
|
||||
final translation =
|
||||
ref.watch(userProfileProvider.select((u) => u?.bibleTranslation)) ??
|
||||
BibleTranslation.esv;
|
||||
final role = ref.watch(userProfileProvider.select((u) => u?.role)) ??
|
||||
UserRole.wife;
|
||||
final isMarried =
|
||||
ref.watch(userProfileProvider.select((u) => u?.isMarried)) ?? false;
|
||||
final averageCycleLength =
|
||||
ref.watch(userProfileProvider.select((u) => u?.averageCycleLength)) ??
|
||||
28;
|
||||
|
||||
final cycleInfo = ref.watch(currentCycleInfoProvider);
|
||||
|
||||
final name = user?.name ?? 'Friend';
|
||||
final phase = cycleInfo['phase'] as CyclePhase;
|
||||
final dayOfCycle = cycleInfo['dayOfCycle'] as int;
|
||||
final cycleLength = user?.averageCycleLength ?? 28;
|
||||
|
||||
// Get scripture for current phase
|
||||
=======
|
||||
final name = ref.watch(userProfileProvider.select((u) => u?.name)) ?? 'Friend';
|
||||
final translation = ref.watch(userProfileProvider.select((u) => u?.bibleTranslation)) ?? BibleTranslation.esv;
|
||||
final role = ref.watch(userProfileProvider.select((u) => u?.role)) ?? UserRole.wife;
|
||||
final isMarried = ref.watch(userProfileProvider.select((u) => u?.isMarried)) ?? false;
|
||||
final averageCycleLength = ref.watch(userProfileProvider.select((u) => u?.averageCycleLength)) ?? 28;
|
||||
|
||||
final cycleInfo = ref.watch(currentCycleInfoProvider);
|
||||
final phase = cycleInfo['phase'] as CyclePhase;
|
||||
final dayOfCycle = cycleInfo['dayOfCycle'] as int;
|
||||
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
final scripture = ScriptureDatabase.getScriptureForPhase(phase.name);
|
||||
final phase = cycleInfo.phase;
|
||||
final dayOfCycle = cycleInfo.dayOfCycle;
|
||||
|
||||
// Watch the scripture provider for the current scripture
|
||||
final scriptureState = ref.watch(scriptureProvider);
|
||||
final scripture = scriptureState.currentScripture;
|
||||
final maxIndex = scriptureState.maxIndex;
|
||||
|
||||
if (scripture == null) {
|
||||
return const Center(child: CircularProgressIndicator()); // Or some error message
|
||||
}
|
||||
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
@@ -157,40 +151,8 @@ class _DashboardTab extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
<<<<<<< HEAD
|
||||
// Greeting
|
||||
_buildGreeting(name),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Cycle Ring
|
||||
Center(
|
||||
child: CycleRing(
|
||||
dayOfCycle: dayOfCycle,
|
||||
totalDays: cycleLength,
|
||||
phase: phase,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Scripture Card
|
||||
ScriptureCard(
|
||||
verse: scripture.verse,
|
||||
reference: scripture.reference,
|
||||
phase: phase,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Quick Log Buttons
|
||||
Text(
|
||||
'Quick Log',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
_buildGreeting(context, name),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
Center(
|
||||
child: CycleRing(
|
||||
dayOfCycle: dayOfCycle,
|
||||
@@ -199,38 +161,65 @@ class _DashboardTab extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
ScriptureCard(
|
||||
verse: scripture.getVerse(translation),
|
||||
reference: scripture.reference,
|
||||
translation: translation.label,
|
||||
phase: phase,
|
||||
onTranslationTap: () => BibleUtils.showTranslationPicker(context, ref),
|
||||
// Main Scripture Card with Navigation
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
ScriptureCard(
|
||||
verse: scripture.getVerse(translation),
|
||||
reference: scripture.reference,
|
||||
translation: translation.label,
|
||||
phase: phase,
|
||||
onTranslationTap: () =>
|
||||
BibleUtils.showTranslationPicker(context, ref),
|
||||
),
|
||||
if (maxIndex != null && maxIndex > 1) ...[
|
||||
Positioned(
|
||||
left: 0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios),
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getPreviousScripture(),
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.arrow_forward_ios),
|
||||
onPressed: () =>
|
||||
ref.read(scriptureProvider.notifier).getNextScripture(),
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (maxIndex != null && maxIndex > 1)
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () => ref.read(scriptureProvider.notifier).getRandomScripture(),
|
||||
icon: const Icon(Icons.shuffle),
|
||||
label: const Text('Random Verse'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.sageGreen,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
Text(
|
||||
'Quick Log',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const QuickLogButtons(),
|
||||
<<<<<<< HEAD
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Today's Tip - Only show if not just tracking or husband (though husband has own screen)
|
||||
if (user?.role == UserRole.wife)
|
||||
TipCard(phase: phase, isMarried: user?.isMarried ?? false),
|
||||
=======
|
||||
const SizedBox(height: 24),
|
||||
|
||||
if (role == UserRole.wife)
|
||||
TipCard(phase: phase, isMarried: isMarried),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
@@ -238,12 +227,8 @@ class _DashboardTab extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
Widget _buildGreeting(String name) {
|
||||
=======
|
||||
Widget _buildGreeting(BuildContext context, String name) {
|
||||
final theme = Theme.of(context);
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
final hour = DateTime.now().hour;
|
||||
String greeting;
|
||||
if (hour < 12) {
|
||||
@@ -264,26 +249,15 @@ class _DashboardTab extends ConsumerWidget {
|
||||
'$greeting,',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 16,
|
||||
<<<<<<< HEAD
|
||||
color: AppColors.warmGray,
|
||||
=======
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
Text(
|
||||
name,
|
||||
<<<<<<< HEAD
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
style: theme.textTheme.displaySmall?.copyWith(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -293,64 +267,41 @@ class _DashboardTab extends ConsumerWidget {
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
<<<<<<< HEAD
|
||||
color: AppColors.blushPink,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.notifications_outlined,
|
||||
color: AppColors.rose,
|
||||
=======
|
||||
color: theme.colorScheme.primaryContainer.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.notifications_outlined,
|
||||
color: theme.colorScheme.primary,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
// Placeholder _calculateCycleInfo removed as it's now in CycleService
|
||||
=======
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
}
|
||||
|
||||
class _SettingsTab extends ConsumerWidget {
|
||||
final VoidCallback? onReset;
|
||||
const _SettingsTab({this.onReset});
|
||||
|
||||
Widget _buildSettingsTile(BuildContext context, IconData icon, String title, {VoidCallback? onTap}) {
|
||||
Widget _buildSettingsTile(BuildContext context, IconData icon, String title,
|
||||
{VoidCallback? onTap}) {
|
||||
return ListTile(
|
||||
<<<<<<< HEAD
|
||||
leading: Icon(icon, color: AppColors.charcoal),
|
||||
title: Text(
|
||||
title,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 16,
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
trailing: Icon(Icons.chevron_right, color: AppColors.lightGray),
|
||||
=======
|
||||
leading: Icon(icon, color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8)),
|
||||
leading: Icon(icon,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8)),
|
||||
title: Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right, color: AppColors.lightGray),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
onTap: onTap ?? () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Settings coming soon!')),
|
||||
);
|
||||
},
|
||||
onTap: onTap ??
|
||||
() {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Settings coming soon!')),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -359,11 +310,14 @@ class _SettingsTab extends ConsumerWidget {
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Reset App?'),
|
||||
content: const Text('This will clear all data and return you to onboarding. Are you sure?'),
|
||||
content: const Text(
|
||||
'This will clear all data and return you to onboarding. Are you sure?'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('Reset', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
@@ -373,7 +327,7 @@ class _SettingsTab extends ConsumerWidget {
|
||||
if (confirmed == true) {
|
||||
await ref.read(userProfileProvider.notifier).clearProfile();
|
||||
await ref.read(cycleEntriesProvider.notifier).clearEntries();
|
||||
|
||||
|
||||
if (context.mounted) {
|
||||
onReset?.call();
|
||||
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
|
||||
@@ -383,14 +337,19 @@ class _SettingsTab extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
<<<<<<< HEAD
|
||||
final user = ref.watch(userProfileProvider);
|
||||
=======
|
||||
final name = ref.watch(userProfileProvider.select((u) => u?.name)) ?? 'Guest';
|
||||
final roleSymbol = ref.watch(userProfileProvider.select((u) => u?.role)) == UserRole.husband ? 'HUSBAND' : null;
|
||||
final relationshipStatus = ref.watch(userProfileProvider.select((u) => u?.relationshipStatus.name.toUpperCase())) ?? 'SINGLE';
|
||||
final translationLabel = ref.watch(userProfileProvider.select((u) => u?.bibleTranslation.label)) ?? 'ESV';
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
final name =
|
||||
ref.watch(userProfileProvider.select((u) => u?.name)) ?? 'Guest';
|
||||
final roleSymbol =
|
||||
ref.watch(userProfileProvider.select((u) => u?.role)) ==
|
||||
UserRole.husband
|
||||
? 'HUSBAND'
|
||||
: null;
|
||||
final relationshipStatus = ref.watch(userProfileProvider
|
||||
.select((u) => u?.relationshipStatus.name.toUpperCase())) ??
|
||||
'SINGLE';
|
||||
final translationLabel =
|
||||
ref.watch(userProfileProvider.select((u) => u?.bibleTranslation.label)) ??
|
||||
'ESV';
|
||||
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
@@ -400,43 +359,21 @@ class _SettingsTab extends ConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
'Settings',
|
||||
<<<<<<< HEAD
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
=======
|
||||
style: Theme.of(context).textTheme.displayMedium?.copyWith(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
),
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Profile Card
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.charcoal.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
=======
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Theme.of(context).colorScheme.outline.withOpacity(0.05)),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
border: Border.all(
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline.withOpacity(0.05)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -445,7 +382,10 @@ class _SettingsTab extends ConsumerWidget {
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [AppColors.blushPink, AppColors.rose.withOpacity(0.7)],
|
||||
colors: [
|
||||
AppColors.blushPink,
|
||||
AppColors.rose.withOpacity(0.7)
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
@@ -453,11 +393,7 @@ class _SettingsTab extends ConsumerWidget {
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
<<<<<<< HEAD
|
||||
user?.name.isNotEmpty == true ? user!.name[0].toUpperCase() : '?',
|
||||
=======
|
||||
name.isNotEmpty ? name[0].toUpperCase() : '?',
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -472,28 +408,15 @@ class _SettingsTab extends ConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
<<<<<<< HEAD
|
||||
user?.name ?? 'Guest',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.charcoal,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
user?.role == UserRole.husband
|
||||
? 'HUSBAND'
|
||||
: (user?.relationshipStatus.name.toUpperCase() ?? 'SINGLE'),
|
||||
=======
|
||||
name,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
style:
|
||||
Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
roleSymbol ?? relationshipStatus,
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
letterSpacing: 1,
|
||||
@@ -503,56 +426,48 @@ class _SettingsTab extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
Icon(Icons.chevron_right, color: AppColors.warmGray),
|
||||
=======
|
||||
const Icon(Icons.chevron_right, color: AppColors.warmGray),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Settings Groups
|
||||
_buildSettingsGroup('Preferences', [
|
||||
_buildSettingsTile(context, Icons.notifications_outlined, 'Notifications'),
|
||||
=======
|
||||
_buildSettingsGroup(context, 'Preferences', [
|
||||
_buildSettingsTile(context, Icons.notifications_outlined, 'Notifications'),
|
||||
_buildSettingsTile(
|
||||
context,
|
||||
Icons.book_outlined,
|
||||
context, Icons.notifications_outlined, 'Notifications'),
|
||||
_buildSettingsTile(
|
||||
context,
|
||||
Icons.book_outlined,
|
||||
'Bible Version ($translationLabel)',
|
||||
onTap: () => BibleUtils.showTranslationPicker(context, ref),
|
||||
),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_buildSettingsTile(context, Icons.palette_outlined, 'Appearance'),
|
||||
_buildSettingsTile(context, Icons.lock_outline, 'Privacy'),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
<<<<<<< HEAD
|
||||
_buildSettingsGroup('Cycle', [
|
||||
=======
|
||||
_buildSettingsGroup(context, 'Cycle', [
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_buildSettingsTile(context, Icons.calendar_today_outlined, 'Cycle Settings'),
|
||||
_buildSettingsTile(context, Icons.trending_up_outlined, 'Cycle History'),
|
||||
_buildSettingsTile(context, Icons.download_outlined, 'Export Data'),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
<<<<<<< HEAD
|
||||
_buildSettingsGroup('Account', [
|
||||
=======
|
||||
_buildSettingsGroup(context, 'Account', [
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
_buildSettingsTile(
|
||||
context,
|
||||
Icons.logout,
|
||||
'Reset App / Logout',
|
||||
onTap: () => _resetApp(context, ref)
|
||||
context,
|
||||
Icons.favorite_border,
|
||||
'My Favorites',
|
||||
onTap: () => _showFavoritesDialog(context, ref),
|
||||
),
|
||||
_buildSettingsTile(context, Icons.lock_outline, 'Privacy'),
|
||||
_buildSettingsTile(
|
||||
context,
|
||||
Icons.share_outlined,
|
||||
'Share with Husband',
|
||||
onTap: () => _showShareDialog(context, ref),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup(context, 'Cycle', [
|
||||
_buildSettingsTile(
|
||||
context, Icons.calendar_today_outlined, 'Cycle Settings'),
|
||||
_buildSettingsTile(
|
||||
context, Icons.trending_up_outlined, 'Cycle History'),
|
||||
_buildSettingsTile(
|
||||
context, Icons.download_outlined, 'Export Data'),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_buildSettingsGroup(context, 'Account', [
|
||||
_buildSettingsTile(context, Icons.logout, 'Reset App / Logout',
|
||||
onTap: () => _resetApp(context, ref)),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
@@ -561,11 +476,8 @@ class _SettingsTab extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
Widget _buildSettingsGroup(String title, List<Widget> tiles) {
|
||||
=======
|
||||
Widget _buildSettingsGroup(BuildContext context, String title, List<Widget> tiles) {
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
Widget _buildSettingsGroup(
|
||||
BuildContext context, String title, List<Widget> tiles) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -581,14 +493,10 @@ class _SettingsTab extends ConsumerWidget {
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
<<<<<<< HEAD
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
=======
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Theme.of(context).colorScheme.outline.withOpacity(0.05)),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.05)),
|
||||
),
|
||||
child: Column(
|
||||
children: tiles,
|
||||
@@ -597,8 +505,121 @@ class _SettingsTab extends ConsumerWidget {
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
=======
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
void _showFavoritesDialog(BuildContext context, WidgetRef ref) {
|
||||
final userProfile = ref.read(userProfileProvider);
|
||||
if (userProfile == null) return;
|
||||
|
||||
final controller = TextEditingController(
|
||||
text: userProfile.favoriteFoods?.join(', ') ?? '',
|
||||
);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('My Favorites', style: GoogleFonts.outfit(fontWeight: FontWeight.bold)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'List your favorite comfort foods, snacks, or flowers so your husband knows what to get you!',
|
||||
style: GoogleFonts.outfit(fontSize: 13, color: AppColors.warmGray),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: controller,
|
||||
maxLines: 3,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'e.g., Dark Chocolate, Sushi, Sunflowers...',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
final favorites = controller.text
|
||||
.split(',')
|
||||
.map((e) => e.trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
final updatedProfile = userProfile.copyWith(favoriteFoods: favorites);
|
||||
ref.read(userProfileProvider.notifier).updateProfile(updatedProfile);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Save'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showShareDialog(BuildContext context, WidgetRef ref) {
|
||||
// Generate a simple pairing code (in a real app, this would be stored/validated)
|
||||
final userProfile = ref.read(userProfileProvider);
|
||||
final pairingCode = userProfile?.id?.substring(0, 6).toUpperCase() ?? 'ABC123';
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
const Icon(Icons.share_outlined, color: AppColors.sageGreen),
|
||||
const SizedBox(width: 8),
|
||||
const Text('Share with Husband'),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Share this code with your husband so he can connect to your cycle data:',
|
||||
style: GoogleFonts.outfit(fontSize: 14, color: AppColors.warmGray),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.sageGreen.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.sageGreen.withOpacity(0.3)),
|
||||
),
|
||||
child: SelectableText(
|
||||
pairingCode,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 4,
|
||||
color: AppColors.sageGreen,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'He can enter this in his app under Settings > Connect with Wife.',
|
||||
style: GoogleFonts.outfit(fontSize: 12, color: AppColors.warmGray),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.sageGreen,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: const Text('Done'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:christian_period_tracker/models/user_profile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
@@ -5,6 +6,10 @@ import '../../models/cycle_entry.dart';
|
||||
import '../../models/scripture.dart';
|
||||
import '../../providers/user_provider.dart';
|
||||
import '../../services/cycle_service.dart';
|
||||
import '../../services/mock_data_service.dart'; // Import mock service
|
||||
import '../calendar/calendar_screen.dart'; // Import calendar
|
||||
import 'husband_notes_screen.dart'; // Import notes screen
|
||||
import 'learn_article_screen.dart'; // Import learn article screen
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
/// Husband's companion app main screen
|
||||
@@ -28,10 +33,8 @@ class _HusbandHomeScreenState extends ConsumerState<HusbandHomeScreen> {
|
||||
index: _selectedIndex,
|
||||
children: [
|
||||
const _HusbandDashboard(),
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
const _HusbandWifeStatus(),
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
const CalendarScreen(readOnly: true), // Reused Calendar
|
||||
const HusbandNotesScreen(), // Notes Screen
|
||||
const _HusbandTipsScreen(),
|
||||
const _HusbandLearnScreen(),
|
||||
const _HusbandSettingsScreen(),
|
||||
@@ -62,14 +65,16 @@ class _HusbandHomeScreenState extends ConsumerState<HusbandHomeScreen> {
|
||||
label: 'Home',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
icon: Icon(Icons.favorite_border),
|
||||
activeIcon: Icon(Icons.favorite),
|
||||
label: 'Status',
|
||||
icon: Icon(Icons.calendar_month_outlined),
|
||||
activeIcon: Icon(Icons.calendar_month),
|
||||
label: 'Calendar',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.note_alt_outlined),
|
||||
activeIcon: Icon(Icons.note_alt),
|
||||
label: 'Notes',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
icon: Icon(Icons.lightbulb_outline),
|
||||
activeIcon: Icon(Icons.lightbulb),
|
||||
label: 'Tips',
|
||||
@@ -121,20 +126,39 @@ class _HusbandHomeScreenState extends ConsumerState<HusbandHomeScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
class _HusbandDashboard extends ConsumerWidget {
|
||||
class _HusbandDashboard extends ConsumerStatefulWidget {
|
||||
const _HusbandDashboard();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<_HusbandDashboard> createState() => _HusbandDashboardState();
|
||||
}
|
||||
|
||||
class _HusbandDashboardState extends ConsumerState<_HusbandDashboard> {
|
||||
Scripture? _currentScripture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadNewVerse();
|
||||
}
|
||||
|
||||
void _loadNewVerse() {
|
||||
setState(() {
|
||||
_currentScripture = ScriptureDatabase().getHusbandScripture();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = ref.watch(userProfileProvider);
|
||||
final cycleInfo = ref.watch(currentCycleInfoProvider);
|
||||
|
||||
final wifeName = user?.partnerName ?? "Wife";
|
||||
final phase = cycleInfo['phase'] as CyclePhase;
|
||||
final dayOfCycle = cycleInfo['dayOfCycle'] as int;
|
||||
final daysUntilPeriod = cycleInfo['daysUntilPeriod'] as int;
|
||||
|
||||
final scripture = ScriptureDatabase.getHusbandScripture();
|
||||
final wifeName = user?.partnerName ?? "Wife";
|
||||
final phase = cycleInfo.phase;
|
||||
final dayOfCycle = cycleInfo.dayOfCycle;
|
||||
final daysUntilPeriod = cycleInfo.daysUntilPeriod;
|
||||
|
||||
final scripture = _currentScripture ?? ScriptureDatabase().getHusbandScripture();
|
||||
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
@@ -311,6 +335,87 @@ class _HusbandDashboard extends ConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Recent Cravings (Dynamic)
|
||||
Builder(
|
||||
builder: (context) {
|
||||
// Get recent cravings from the last 3 days
|
||||
final allEntries = ref.read(cycleEntriesProvider);
|
||||
// Sort by date desc
|
||||
final sortedEntries = List<CycleEntry>.from(allEntries)..sort((a,b) => b.date.compareTo(a.date));
|
||||
|
||||
final recentCravings = <String>{};
|
||||
final now = DateTime.now();
|
||||
for (var entry in sortedEntries) {
|
||||
if (now.difference(entry.date).inDays > 3) break;
|
||||
if (entry.cravings != null) {
|
||||
recentCravings.addAll(entry.cravings!);
|
||||
}
|
||||
}
|
||||
|
||||
if (recentCravings.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: AppColors.rose.withOpacity(0.3)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.rose.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.rose.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.fastfood,
|
||||
color: AppColors.rose,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'She is Craving...',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.navyBlue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: recentCravings.map((craving) => Chip(
|
||||
label: Text(craving),
|
||||
backgroundColor: AppColors.rose.withOpacity(0.1),
|
||||
labelStyle: GoogleFonts.outfit(color: AppColors.navyBlue, fontWeight: FontWeight.w500),
|
||||
side: BorderSide.none,
|
||||
)).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// Scripture for Husbands
|
||||
Container(
|
||||
width: double.infinity,
|
||||
@@ -340,19 +445,47 @@ class _HusbandDashboard extends ConsumerWidget {
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Scripture for Husbands',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.warmGray,
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Scripture for Husbands',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Quick version toggle
|
||||
GestureDetector(
|
||||
onTap: () => _showVersionPicker(context, ref),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.gold.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
user?.bibleTranslation.label ?? 'ESV',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.gold,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Icon(Icons.arrow_drop_down, color: AppColors.gold, size: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'"${scripture.verse}"',
|
||||
'"${scripture.getVerse(user?.bibleTranslation ?? BibleTranslation.esv)}"',
|
||||
style: GoogleFonts.lora(
|
||||
fontSize: 15,
|
||||
fontStyle: FontStyle.italic,
|
||||
@@ -361,13 +494,43 @@ class _HusbandDashboard extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'— ${scripture.reference}',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'— ${scripture.reference}',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: _loadNewVerse,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.gold.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.refresh, color: AppColors.gold, size: 14),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'New Verse',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.gold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -428,6 +591,51 @@ class _HusbandDashboard extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
void _showVersionPicker(BuildContext context, WidgetRef ref) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (context) => Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Choose Translation',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...BibleTranslation.values.map((translation) => ListTile(
|
||||
title: Text(
|
||||
translation.label,
|
||||
style: GoogleFonts.outfit(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: ref.watch(userProfileProvider)?.bibleTranslation == translation
|
||||
? const Icon(Icons.check, color: AppColors.sageGreen)
|
||||
: null,
|
||||
onTap: () async {
|
||||
final profile = ref.read(userProfileProvider);
|
||||
if (profile != null) {
|
||||
await ref.read(userProfileProvider.notifier).updateProfile(
|
||||
profile.copyWith(bibleTranslation: translation),
|
||||
);
|
||||
}
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPrayerPrompt(BuildContext context, CyclePhase phase) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
@@ -527,7 +735,6 @@ class _HusbandTipsScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildTipCategory('During Her Period', [
|
||||
'🏠 Help with household tasks without being asked',
|
||||
'🍵 Bring her favorite comfort drink',
|
||||
@@ -535,7 +742,6 @@ class _HusbandTipsScreen extends StatelessWidget {
|
||||
'🙏 Pray for her physical comfort',
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
_buildTipCategory('Follicular Phase', [
|
||||
'🎉 Plan dates or activities—her energy is returning',
|
||||
'💬 She may be more talkative and social',
|
||||
@@ -543,7 +749,6 @@ class _HusbandTipsScreen extends StatelessWidget {
|
||||
'❤️ Affirm her strengths and beauty',
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
_buildTipCategory('Luteal Phase (PMS)', [
|
||||
'😌 Be patient—PMS may affect her mood',
|
||||
'🍫 Surprise with comfort foods',
|
||||
@@ -551,6 +756,24 @@ class _HusbandTipsScreen extends StatelessWidget {
|
||||
'👂 Listen more, "fix" less',
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Her Favorites Section
|
||||
Consumer(
|
||||
builder: (context, ref, child) {
|
||||
final user = ref.watch(userProfileProvider);
|
||||
final favorites = user?.favoriteFoods;
|
||||
|
||||
if (favorites == null || favorites.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
_buildTipCategory('❤️ Her Favorites (Cheat Sheet)', favorites),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
_buildTipCategory('General Wisdom', [
|
||||
'🗣️ Ask how she\'s feeling—and actually listen',
|
||||
@@ -621,55 +844,60 @@ class _HusbandLearnScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildSection('Understanding Her', [
|
||||
_buildSection(context, 'Understanding Her', [
|
||||
_LearnItem(
|
||||
icon: Icons.loop,
|
||||
title: 'The 4 Phases of Her Cycle',
|
||||
subtitle: 'What\'s happening in her body each month',
|
||||
articleId: 'four_phases',
|
||||
),
|
||||
_LearnItem(
|
||||
icon: Icons.psychology_outlined,
|
||||
title: 'Why Does Her Mood Change?',
|
||||
subtitle: 'Hormones explained simply',
|
||||
articleId: 'mood_changes',
|
||||
),
|
||||
_LearnItem(
|
||||
icon: Icons.medical_information_outlined,
|
||||
title: 'PMS is Real',
|
||||
subtitle: 'Medical facts for supportive husbands',
|
||||
articleId: 'pms_is_real',
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildSection('Biblical Manhood', [
|
||||
_buildSection(context, 'Biblical Manhood', [
|
||||
_LearnItem(
|
||||
icon: Icons.favorite,
|
||||
title: 'Loving Like Christ',
|
||||
subtitle: 'Ephesians 5 in daily practice',
|
||||
articleId: 'loving_like_christ',
|
||||
),
|
||||
_LearnItem(
|
||||
icon: Icons.handshake,
|
||||
title: 'Servant Leadership at Home',
|
||||
subtitle: 'What it really means',
|
||||
articleId: 'servant_leadership',
|
||||
),
|
||||
_LearnItem(
|
||||
icon: Icons.auto_awesome,
|
||||
title: 'Praying for Your Wife',
|
||||
subtitle: 'Practical guide',
|
||||
articleId: 'praying_for_wife',
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
_buildSection('NFP for Husbands', [
|
||||
_buildSection(context, 'NFP for Husbands', [
|
||||
_LearnItem(
|
||||
icon: Icons.show_chart,
|
||||
title: 'Reading the Charts Together',
|
||||
subtitle: 'Understanding fertility signs',
|
||||
articleId: 'reading_charts',
|
||||
),
|
||||
_LearnItem(
|
||||
icon: Icons.schedule,
|
||||
title: 'Abstinence as Spiritual Discipline',
|
||||
subtitle: 'Growing together during fertile days',
|
||||
articleId: 'abstinence_discipline',
|
||||
),
|
||||
]),
|
||||
],
|
||||
@@ -678,7 +906,7 @@ class _HusbandLearnScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSection(String title, List<_LearnItem> items) {
|
||||
Widget _buildSection(BuildContext context, String title, List<_LearnItem> items) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -732,7 +960,14 @@ class _HusbandLearnScreen extends StatelessWidget {
|
||||
Icons.chevron_right,
|
||||
color: AppColors.lightGray,
|
||||
),
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LearnArticleScreen(articleId: item.articleId),
|
||||
),
|
||||
);
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
@@ -746,11 +981,13 @@ class _LearnItem {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String articleId;
|
||||
|
||||
const _LearnItem({
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.articleId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -762,11 +999,14 @@ class _HusbandSettingsScreen extends ConsumerWidget {
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Reset App?'),
|
||||
content: const Text('This will clear all data and return you to onboarding. Are you sure?'),
|
||||
content: const Text(
|
||||
'This will clear all data and return you to onboarding. Are you sure?'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('Reset', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
@@ -776,13 +1016,191 @@ class _HusbandSettingsScreen extends ConsumerWidget {
|
||||
if (confirmed == true) {
|
||||
await ref.read(userProfileProvider.notifier).clearProfile();
|
||||
await ref.read(cycleEntriesProvider.notifier).clearEntries();
|
||||
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadDemoData(BuildContext context, WidgetRef ref) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Load Demo Data?'),
|
||||
content: const Text(
|
||||
'This will populate the app with mock cycle entries and a wife profile.'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child:
|
||||
const Text('Load Data', style: TextStyle(color: Colors.blue)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
final mockService = MockDataService();
|
||||
// Load mock entries
|
||||
final entries = mockService.generateMockCycleEntries();
|
||||
for (var entry in entries) {
|
||||
await ref.read(cycleEntriesProvider.notifier).addEntry(entry);
|
||||
}
|
||||
|
||||
// Update mock profile
|
||||
final mockWife = mockService.generateMockWifeProfile();
|
||||
// Need to preserve current Husband ID and Role but take other data
|
||||
final currentProfile = ref.read(userProfileProvider);
|
||||
if (currentProfile != null) {
|
||||
final updatedProfile = currentProfile.copyWith(
|
||||
partnerName: mockWife.name,
|
||||
averageCycleLength: mockWife.averageCycleLength,
|
||||
averagePeriodLength: mockWife.averagePeriodLength,
|
||||
lastPeriodStartDate: mockWife.lastPeriodStartDate,
|
||||
favoriteFoods: mockWife.favoriteFoods,
|
||||
);
|
||||
await ref.read(userProfileProvider.notifier).updateProfile(updatedProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showTranslationPicker(BuildContext context, WidgetRef ref) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (context) => Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Choose Translation',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...BibleTranslation.values.map((translation) => ListTile(
|
||||
title: Text(
|
||||
translation.label,
|
||||
style: GoogleFonts.outfit(fontWeight: FontWeight.w500),
|
||||
),
|
||||
trailing: ref.watch(userProfileProvider)?.bibleTranslation == translation
|
||||
? const Icon(Icons.check, color: AppColors.sageGreen)
|
||||
: null,
|
||||
onTap: () async {
|
||||
final profile = ref.read(userProfileProvider);
|
||||
if (profile != null) {
|
||||
await ref.read(userProfileProvider.notifier).updateProfile(
|
||||
profile.copyWith(bibleTranslation: translation),
|
||||
);
|
||||
}
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showConnectDialog(BuildContext context, WidgetRef ref) {
|
||||
final codeController = TextEditingController();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
const Icon(Icons.link, color: AppColors.navyBlue),
|
||||
const SizedBox(width: 8),
|
||||
const Text('Connect with Wife'),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Enter the pairing code from your wife\'s app:',
|
||||
style: GoogleFonts.outfit(fontSize: 14, color: AppColors.warmGray),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: codeController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'e.g., ABC123',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
textCapitalization: TextCapitalization.characters,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Your wife can find this code in her Settings under "Share with Husband".',
|
||||
style: GoogleFonts.outfit(fontSize: 12, color: AppColors.warmGray),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final code = codeController.text.trim();
|
||||
if (code.isEmpty) return;
|
||||
|
||||
// In a real app, this would validate the code against a backend
|
||||
// For now, we'll just show a success message and simulate pairing
|
||||
Navigator.pop(context);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Connected! Loading wife\'s data...'),
|
||||
backgroundColor: AppColors.sageGreen,
|
||||
),
|
||||
);
|
||||
|
||||
// Load demo data as simulation of pairing
|
||||
final mockService = MockDataService();
|
||||
final entries = mockService.generateMockCycleEntries();
|
||||
for (var entry in entries) {
|
||||
await ref.read(cycleEntriesProvider.notifier).addEntry(entry);
|
||||
}
|
||||
final mockWife = mockService.generateMockWifeProfile();
|
||||
final currentProfile = ref.read(userProfileProvider);
|
||||
if (currentProfile != null) {
|
||||
final updatedProfile = currentProfile.copyWith(
|
||||
partnerName: mockWife.name,
|
||||
averageCycleLength: mockWife.averageCycleLength,
|
||||
averagePeriodLength: mockWife.averagePeriodLength,
|
||||
lastPeriodStartDate: mockWife.lastPeriodStartDate,
|
||||
favoriteFoods: mockWife.favoriteFoods,
|
||||
);
|
||||
await ref.read(userProfileProvider.notifier).updateProfile(updatedProfile);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.navyBlue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: const Text('Connect'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SafeArea(
|
||||
@@ -808,30 +1226,69 @@ class _HusbandSettingsScreen extends ConsumerWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.notifications_outlined),
|
||||
title: Text('Notifications', style: GoogleFonts.outfit()),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {},
|
||||
leading: const Icon(Icons.notifications_outlined,
|
||||
color: AppColors.navyBlue),
|
||||
title: Text('Notifications',
|
||||
style: GoogleFonts.outfit(fontWeight: FontWeight.w500)),
|
||||
trailing: Switch(value: true, onChanged: (val) {}),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.link_outlined),
|
||||
title: Text('Connection', style: GoogleFonts.outfit()),
|
||||
subtitle: Text('Linked with wife\'s app', style: GoogleFonts.outfit(fontSize: 12)),
|
||||
leading: const Icon(Icons.link,
|
||||
color: AppColors.navyBlue),
|
||||
title: Text('Connect with Wife',
|
||||
style: GoogleFonts.outfit(fontWeight: FontWeight.w500)),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {},
|
||||
onTap: () => _showConnectDialog(context, ref),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout),
|
||||
title: Text('Reset App / Logout', style: GoogleFonts.outfit()),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
leading: const Icon(Icons.menu_book_outlined,
|
||||
color: AppColors.navyBlue),
|
||||
title: Text('Bible Translation',
|
||||
style: GoogleFonts.outfit(fontWeight: FontWeight.w500)),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
ref.watch(userProfileProvider.select((u) => u?.bibleTranslation.label)) ?? 'ESV',
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
const Icon(Icons.chevron_right),
|
||||
],
|
||||
),
|
||||
onTap: () => _showTranslationPicker(context, ref),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.cloud_download_outlined,
|
||||
color: Colors.blue),
|
||||
title: Text('Load Demo Data',
|
||||
style: GoogleFonts.outfit(
|
||||
fontWeight: FontWeight.w500, color: Colors.blue)),
|
||||
onTap: () => _loadDemoData(context, ref),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout, color: Colors.red),
|
||||
title: Text('Reset App',
|
||||
style: GoogleFonts.outfit(
|
||||
fontWeight: FontWeight.w500, color: Colors.red)),
|
||||
onTap: () => _resetApp(context, ref),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.help_outline),
|
||||
title: Text('Help & Support', style: GoogleFonts.outfit()),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -841,8 +1298,7 @@ class _HusbandSettingsScreen extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
class _HusbandWifeStatus extends ConsumerWidget {
|
||||
const _HusbandWifeStatus();
|
||||
|
||||
@@ -851,11 +1307,11 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
final user = ref.watch(userProfileProvider);
|
||||
final cycleInfo = ref.watch(currentCycleInfoProvider);
|
||||
final entries = ref.watch(cycleEntriesProvider);
|
||||
|
||||
|
||||
final wifeName = user?.partnerName ?? "Wife";
|
||||
final phase = cycleInfo['phase'] as CyclePhase;
|
||||
final dayOfCycle = cycleInfo['dayOfCycle'] as int;
|
||||
|
||||
final phase = cycleInfo.phase;
|
||||
final dayOfCycle = cycleInfo.dayOfCycle;
|
||||
|
||||
// Find today's entry
|
||||
final todayEntry = entries.firstWhere(
|
||||
(e) => DateUtils.isSameDay(e.date, DateTime.now()),
|
||||
@@ -960,16 +1416,20 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.navyBlue.withOpacity(0.03),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: AppColors.navyBlue.withOpacity(0.05)),
|
||||
border:
|
||||
Border.all(color: AppColors.navyBlue.withOpacity(0.05)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
if (todayEntry.mood != null)
|
||||
_buildLogTile(Icons.emoji_emotions_outlined, 'Mood', '${todayEntry.mood!.emoji} ${todayEntry.mood!.label}'),
|
||||
_buildLogTile(Icons.emoji_emotions_outlined, 'Mood',
|
||||
'${todayEntry.mood!.emoji} ${todayEntry.mood!.label}'),
|
||||
if (todayEntry.hasSymptoms)
|
||||
_buildLogTile(Icons.healing_outlined, 'Symptoms', _getSymptomsSummary(todayEntry)),
|
||||
_buildLogTile(Icons.healing_outlined, 'Symptoms',
|
||||
_getSymptomsSummary(todayEntry)),
|
||||
if (todayEntry.energyLevel != null)
|
||||
_buildLogTile(Icons.flash_on, 'Energy', '${todayEntry.energyLevel}/5'),
|
||||
_buildLogTile(Icons.flash_on, 'Energy',
|
||||
'${todayEntry.energyLevel}/5'),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -986,8 +1446,9 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
..._generateChecklist(todayEntry, phase).map((item) => _buildCheckItem(item)),
|
||||
|
||||
..._generateChecklist(todayEntry, phase)
|
||||
.map((item) => _buildCheckItem(item)),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
@@ -1050,7 +1511,8 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.check_circle_outline, color: AppColors.sageGreen, size: 20),
|
||||
Icon(Icons.check_circle_outline,
|
||||
color: AppColors.sageGreen, size: 20),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
@@ -1065,7 +1527,8 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
|
||||
String _getSymptomsSummary(CycleEntry entry) {
|
||||
List<String> s = [];
|
||||
if (entry.crampIntensity != null && entry.crampIntensity! > 0) s.add('Cramps');
|
||||
if (entry.crampIntensity != null && entry.crampIntensity! > 0)
|
||||
s.add('Cramps');
|
||||
if (entry.hasHeadache) s.add('Headache');
|
||||
if (entry.hasBloating) s.add('Bloating');
|
||||
if (entry.hasFatigue) s.add('Fatigue');
|
||||
@@ -1075,7 +1538,7 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
|
||||
List<String> _generateChecklist(CycleEntry entry, CyclePhase phase) {
|
||||
List<String> list = [];
|
||||
|
||||
|
||||
// Symptom-based tips
|
||||
if (entry.crampIntensity != null && entry.crampIntensity! >= 3) {
|
||||
list.add('Bring her a heating pad or hot water bottle.');
|
||||
@@ -1083,7 +1546,8 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
if (entry.hasHeadache) {
|
||||
list.add('Suggest some quiet time with dimmed lights.');
|
||||
}
|
||||
if (entry.hasFatigue || (entry.energyLevel != null && entry.energyLevel! <= 2)) {
|
||||
if (entry.hasFatigue ||
|
||||
(entry.energyLevel != null && entry.energyLevel! <= 2)) {
|
||||
list.add('Take over dinner or household chores tonight.');
|
||||
}
|
||||
if (entry.mood == MoodLevel.sad || entry.mood == MoodLevel.verySad) {
|
||||
@@ -1111,8 +1575,8 @@ class _HusbandWifeStatus extends ConsumerWidget {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return list.take(4).toList();
|
||||
}
|
||||
}
|
||||
>>>>>>> 6742220 (Your commit message here)
|
||||
|
||||
|
||||
96
lib/screens/husband/husband_notes_screen.dart
Normal file
96
lib/screens/husband/husband_notes_screen.dart
Normal file
@@ -0,0 +1,96 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../models/cycle_entry.dart';
|
||||
import '../../providers/user_provider.dart';
|
||||
|
||||
class HusbandNotesScreen extends ConsumerWidget {
|
||||
const HusbandNotesScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final entries = ref.watch(cycleEntriesProvider);
|
||||
final notesEntries = entries
|
||||
.where((entry) =>
|
||||
(entry.notes != null && entry.notes!.isNotEmpty) ||
|
||||
(entry.husbandNotes != null && entry.husbandNotes!.isNotEmpty))
|
||||
.toList();
|
||||
|
||||
// Sort entries by date, newest first
|
||||
notesEntries.sort((a, b) => b.date.compareTo(a.date));
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Notes'),
|
||||
),
|
||||
body: notesEntries.isEmpty
|
||||
? const Center(
|
||||
child: Text('No notes have been logged yet.'),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: notesEntries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final entry = notesEntries[index];
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
DateFormat.yMMMMd().format(entry.date),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (entry.notes != null && entry.notes!.isNotEmpty)
|
||||
_NoteSection(
|
||||
title: 'Her Notes',
|
||||
content: entry.notes!,
|
||||
),
|
||||
if (entry.husbandNotes != null && entry.husbandNotes!.isNotEmpty)
|
||||
_NoteSection(
|
||||
title: 'Your Notes',
|
||||
content: entry.husbandNotes!,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NoteSection extends StatelessWidget {
|
||||
final String title;
|
||||
final String content;
|
||||
|
||||
const _NoteSection({required this.title, required this.content});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
content,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
156
lib/screens/husband/learn_article_screen.dart
Normal file
156
lib/screens/husband/learn_article_screen.dart
Normal file
@@ -0,0 +1,156 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../data/learn_content.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
|
||||
/// Screen to display full learn article content
|
||||
class LearnArticleScreen extends StatelessWidget {
|
||||
final String articleId;
|
||||
|
||||
const LearnArticleScreen({super.key, required this.articleId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final article = LearnContent.getArticle(articleId);
|
||||
|
||||
if (article == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Article Not Found')),
|
||||
body: const Center(child: Text('Article not found')),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.warmCream,
|
||||
appBar: AppBar(
|
||||
backgroundColor: AppColors.warmCream,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: AppColors.navyBlue),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
article.category,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Title
|
||||
Text(
|
||||
article.title,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.navyBlue,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
article.subtitle,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 15,
|
||||
color: AppColors.warmGray,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Divider
|
||||
Container(
|
||||
height: 3,
|
||||
width: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.gold,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Sections
|
||||
...article.sections.map((section) => _buildSection(section)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSection(LearnSection section) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (section.heading != null) ...[
|
||||
Text(
|
||||
section.heading!,
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.navyBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
_buildRichText(section.content),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRichText(String content) {
|
||||
// Handle basic markdown-like formatting
|
||||
final List<InlineSpan> spans = [];
|
||||
final RegExp boldPattern = RegExp(r'\*\*(.*?)\*\*');
|
||||
|
||||
int currentIndex = 0;
|
||||
for (final match in boldPattern.allMatches(content)) {
|
||||
// Add text before the match
|
||||
if (match.start > currentIndex) {
|
||||
spans.add(TextSpan(
|
||||
text: content.substring(currentIndex, match.start),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 15,
|
||||
color: AppColors.charcoal,
|
||||
height: 1.7,
|
||||
),
|
||||
));
|
||||
}
|
||||
// Add bold text
|
||||
spans.add(TextSpan(
|
||||
text: match.group(1),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.navyBlue,
|
||||
height: 1.7,
|
||||
),
|
||||
));
|
||||
currentIndex = match.end;
|
||||
}
|
||||
|
||||
// Add remaining text
|
||||
if (currentIndex < content.length) {
|
||||
spans.add(TextSpan(
|
||||
text: content.substring(currentIndex),
|
||||
style: GoogleFonts.outfit(
|
||||
fontSize: 15,
|
||||
color: AppColors.charcoal,
|
||||
height: 1.7,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(children: spans),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,7 @@ class _SplashScreenState extends ConsumerState<SplashScreen> with SingleTickerPr
|
||||
_controller.forward();
|
||||
|
||||
// Navigate after splash
|
||||
Future.delayed(const Duration(milliseconds: 2500), () {
|
||||
Future.delayed(const Duration(milliseconds: 1200), () {
|
||||
_navigateToNextScreen();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user