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