From 9ae77e7ab0fbbab58da0131f766ce3167dfec2ba Mon Sep 17 00:00:00 2001 From: Sterlen Date: Mon, 5 Jan 2026 18:00:44 -0600 Subject: [PATCH] Refactor Husband Settings to dedicated screen and add Appearance option --- lib/screens/husband/husband_home_screen.dart | 19 +- .../husband/husband_settings_screen.dart | 384 ++++++++++++++++++ 2 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 lib/screens/husband/husband_settings_screen.dart diff --git a/lib/screens/husband/husband_home_screen.dart b/lib/screens/husband/husband_home_screen.dart index 928c861..114922e 100644 --- a/lib/screens/husband/husband_home_screen.dart +++ b/lib/screens/husband/husband_home_screen.dart @@ -12,6 +12,7 @@ import 'husband_notes_screen.dart'; // Import notes screen import 'learn_article_screen.dart'; // Import learn article screen import 'husband_devotional_screen.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'husband_settings_screen.dart'; /// Husband's companion app main screen class HusbandHomeScreen extends ConsumerStatefulWidget { @@ -38,7 +39,7 @@ class _HusbandHomeScreenState extends ConsumerState { const HusbandDevotionalScreen(), // Devotional & Planning const _HusbandTipsScreen(), const _HusbandLearnScreen(), - const _HusbandSettingsScreen(), + const HusbandSettingsScreen(), ], ), bottomNavigationBar: Container( @@ -1443,6 +1444,22 @@ class _HusbandSettingsScreen extends ConsumerWidget { ), onTap: () => _showTranslationPicker(context, ref), ), + const Divider(height: 1), + ListTile( + leading: const Icon(Icons.palette_outlined, + color: AppColors.navyBlue), + title: Text('Appearance', + style: GoogleFonts.outfit(fontWeight: FontWeight.w500)), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AppearanceScreen(), + ), + ); + }, + ), ], ), ), diff --git a/lib/screens/husband/husband_settings_screen.dart b/lib/screens/husband/husband_settings_screen.dart new file mode 100644 index 0000000..21942db --- /dev/null +++ b/lib/screens/husband/husband_settings_screen.dart @@ -0,0 +1,384 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:google_fonts/google_fonts.dart'; +import '../../theme/app_theme.dart'; +import '../../models/user_profile.dart'; +import '../../providers/user_provider.dart'; +import '../../services/mock_data_service.dart'; +import '../../providers/cycle_provider.dart'; // Ensure cycleEntriesProvider is available +import '../settings/appearance_screen.dart'; + +class HusbandSettingsScreen extends ConsumerWidget { + const HusbandSettingsScreen({super.key}); + + Future _resetApp(BuildContext context, WidgetRef ref) async { + final confirmed = await showDialog( + 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?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Cancel')), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: const Text('Reset', style: TextStyle(color: Colors.red)), + ), + ], + ), + ); + + 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 _loadDemoData(BuildContext context, WidgetRef ref) async { + final confirmed = await showDialog( + 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(); + 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); + } + + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Demo data loaded')), + ); + } + } + } + + 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(); + bool shareDevotional = true; + + showDialog( + context: context, + builder: (context) => StatefulBuilder( + builder: (context, setState) => 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, + 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), + ), + const SizedBox(height: 24), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 24, + width: 24, + child: Checkbox( + value: shareDevotional, + onChanged: (val) => setState(() => shareDevotional = val ?? true), + activeColor: AppColors.navyBlue, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Share Devotional Plans', + style: GoogleFonts.outfit(fontWeight: FontWeight.bold, fontSize: 14, color: AppColors.charcoal), + ), + Text( + 'Allow her to see the teaching plans you create.', + 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(); + + Navigator.pop(context); + + // Update preference + final user = ref.read(userProfileProvider); + if (user != null) { + await ref.read(userProfileProvider.notifier).updateProfile( + user.copyWith(isDataShared: shareDevotional) + ); + } + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Settings updated & Connected!'), + backgroundColor: AppColors.sageGreen, + ), + ); + + if (code.isNotEmpty) { + // Load demo data as simulation + 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( + isDataShared: shareDevotional, + 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) { + // Theme aware colors + final isDark = Theme.of(context).brightness == Brightness.dark; + final cardColor = Theme.of(context).cardTheme.color; // Using theme card color + final textColor = Theme.of(context).textTheme.bodyLarge?.color; + + return SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Settings', + style: GoogleFonts.outfit( + fontSize: 28, + fontWeight: FontWeight.w600, + color: Theme.of(context).textTheme.displayMedium?.color ?? AppColors.navyBlue, + ), + ), + const SizedBox(height: 24), + Container( + decoration: BoxDecoration( + color: cardColor, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + ListTile( + leading: Icon(Icons.notifications_outlined, + color: Theme.of(context).colorScheme.primary), + title: Text('Notifications', + style: GoogleFonts.outfit(fontWeight: FontWeight.w500, color: textColor)), + trailing: Switch(value: true, onChanged: (val) {}), + ), + const Divider(height: 1), + ListTile( + leading: Icon(Icons.link, + color: Theme.of(context).colorScheme.primary), + title: Text('Connect with Wife', + style: GoogleFonts.outfit(fontWeight: FontWeight.w500, color: textColor)), + trailing: Icon(Icons.chevron_right, color: Theme.of(context).disabledColor), + onTap: () => _showConnectDialog(context, ref), + ), + const Divider(height: 1), + ListTile( + leading: Icon(Icons.menu_book_outlined, + color: Theme.of(context).colorScheme.primary), + title: Text('Bible Translation', + style: GoogleFonts.outfit(fontWeight: FontWeight.w500, color: textColor)), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + ref.watch(userProfileProvider.select((u) => u?.bibleTranslation.label)) ?? 'ESV', + style: GoogleFonts.outfit( + fontSize: 14, + color: Theme.of(context).textTheme.bodyMedium?.color ?? AppColors.warmGray, + ), + ), + Icon(Icons.chevron_right, color: Theme.of(context).disabledColor), + ], + ), + onTap: () => _showTranslationPicker(context, ref), + ), + const Divider(height: 1), + ListTile( + leading: Icon(Icons.palette_outlined, + color: Theme.of(context).colorScheme.primary), + title: Text('Appearance', + style: GoogleFonts.outfit(fontWeight: FontWeight.w500, color: textColor)), + trailing: Icon(Icons.chevron_right, color: Theme.of(context).disabledColor), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AppearanceScreen(), + ), + ); + }, + ), + ], + ), + ), + const SizedBox(height: 20), + Container( + decoration: BoxDecoration( + color: cardColor, + 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), + ), + ], + ), + ), + ], + ), + ), + ); + } +}