939 lines
32 KiB
Dart
939 lines
32 KiB
Dart
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 '../../models/cycle_entry.dart';
|
|
import '../../models/scripture.dart';
|
|
import '../calendar/calendar_screen.dart';
|
|
import '../log/log_screen.dart';
|
|
import '../log/pad_tracker_screen.dart';
|
|
import '../devotional/devotional_screen.dart';
|
|
import '../settings/appearance_screen.dart';
|
|
import '../settings/cycle_settings_screen.dart';
|
|
import '../settings/relationship_settings_screen.dart';
|
|
import '../settings/goal_settings_screen.dart';
|
|
import '../settings/cycle_history_screen.dart';
|
|
import '../settings/sharing_settings_screen.dart';
|
|
import '../settings/notification_settings_screen.dart';
|
|
import '../settings/privacy_settings_screen.dart';
|
|
import '../settings/supplies_settings_screen.dart';
|
|
import '../settings/export_data_screen.dart';
|
|
import '../learn/wife_learn_screen.dart';
|
|
import '../../widgets/tip_card.dart';
|
|
import '../../widgets/cycle_ring.dart';
|
|
import '../../widgets/scripture_card.dart';
|
|
import '../../widgets/pad_tracker_card.dart';
|
|
import '../../widgets/quick_log_buttons.dart';
|
|
import '../../providers/user_provider.dart';
|
|
import '../../providers/navigation_provider.dart';
|
|
import '../../services/cycle_service.dart';
|
|
import '../../services/bible_utils.dart';
|
|
import '../../providers/scripture_provider.dart';
|
|
|
|
class HomeScreen extends ConsumerWidget {
|
|
const HomeScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final selectedIndex = ref.watch(navigationProvider);
|
|
final isPadTrackingEnabled = ref.watch(
|
|
userProfileProvider.select((u) => u?.isPadTrackingEnabled ?? false));
|
|
|
|
final List<Widget> tabs;
|
|
final List<BottomNavigationBarItem> navBarItems;
|
|
|
|
if (isPadTrackingEnabled) {
|
|
tabs = [
|
|
const _DashboardTab(),
|
|
const CalendarScreen(),
|
|
const PadTrackerScreen(),
|
|
const LogScreen(),
|
|
const DevotionalScreen(),
|
|
const WifeLearnScreen(),
|
|
_SettingsTab(
|
|
onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
|
];
|
|
navBarItems = [
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.home_outlined),
|
|
activeIcon: Icon(Icons.home),
|
|
label: 'Home'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.calendar_today_outlined),
|
|
activeIcon: Icon(Icons.calendar_today),
|
|
label: 'Calendar'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.inventory_2_outlined),
|
|
activeIcon: Icon(Icons.inventory_2),
|
|
label: 'Supplies'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.add_circle_outline),
|
|
activeIcon: Icon(Icons.add_circle),
|
|
label: 'Log'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.menu_book_outlined),
|
|
activeIcon: Icon(Icons.menu_book),
|
|
label: 'Devotional'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.school_outlined),
|
|
activeIcon: Icon(Icons.school),
|
|
label: 'Learn'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.settings_outlined),
|
|
activeIcon: Icon(Icons.settings),
|
|
label: 'Settings'),
|
|
];
|
|
} else {
|
|
tabs = [
|
|
const _DashboardTab(),
|
|
const CalendarScreen(),
|
|
const DevotionalScreen(),
|
|
const LogScreen(),
|
|
const WifeLearnScreen(),
|
|
_SettingsTab(
|
|
onReset: () => ref.read(navigationProvider.notifier).setIndex(0)),
|
|
];
|
|
navBarItems = [
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.home_outlined),
|
|
activeIcon: Icon(Icons.home),
|
|
label: 'Home'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.calendar_today_outlined),
|
|
activeIcon: Icon(Icons.calendar_today),
|
|
label: 'Calendar'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.menu_book_outlined),
|
|
activeIcon: Icon(Icons.menu_book),
|
|
label: 'Devotional'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.add_circle_outline),
|
|
activeIcon: Icon(Icons.add_circle),
|
|
label: 'Log'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.school_outlined),
|
|
activeIcon: Icon(Icons.school),
|
|
label: 'Learn'),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(Icons.settings_outlined),
|
|
activeIcon: Icon(Icons.settings),
|
|
label: 'Settings'),
|
|
];
|
|
}
|
|
|
|
return Scaffold(
|
|
body: IndexedStack(
|
|
index: selectedIndex >= tabs.length ? 0 : selectedIndex,
|
|
children: tabs,
|
|
),
|
|
bottomNavigationBar: Container(
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).bottomNavigationBarTheme.backgroundColor,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: (Theme.of(context).brightness == Brightness.dark
|
|
? Colors.black
|
|
: AppColors.charcoal)
|
|
.withOpacity(0.1),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, -2),
|
|
),
|
|
],
|
|
),
|
|
child: BottomNavigationBar(
|
|
currentIndex: selectedIndex >= tabs.length ? 0 : selectedIndex,
|
|
onTap: (index) =>
|
|
ref.read(navigationProvider.notifier).setIndex(index),
|
|
items: navBarItems,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _DashboardTab extends ConsumerStatefulWidget {
|
|
const _DashboardTab({super.key});
|
|
|
|
@override
|
|
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 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(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildGreeting(context, name),
|
|
const SizedBox(height: 24),
|
|
Center(
|
|
child: CycleRing(
|
|
dayOfCycle: dayOfCycle,
|
|
totalDays: averageCycleLength,
|
|
phase: phase,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
const PadTrackerCard(),
|
|
const SizedBox(height: 32),
|
|
// 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: Theme.of(context).colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Quick Log',
|
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
const QuickLogButtons(),
|
|
const SizedBox(height: 24),
|
|
if (role == UserRole.wife) _buildWifeTipsSection(context),
|
|
const SizedBox(height: 20),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildGreeting(BuildContext context, String name) {
|
|
final theme = Theme.of(context);
|
|
final hour = DateTime.now().hour;
|
|
String greeting;
|
|
if (hour < 12) {
|
|
greeting = 'Good morning';
|
|
} else if (hour < 17) {
|
|
greeting = 'Good afternoon';
|
|
} else {
|
|
greeting = 'Good evening';
|
|
}
|
|
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'$greeting,',
|
|
style: GoogleFonts.outfit(
|
|
fontSize: 16,
|
|
color: theme.colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
Text(
|
|
name,
|
|
style: theme.textTheme.displaySmall?.copyWith(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.w600,
|
|
color: theme.colorScheme.onSurface,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
width: 48,
|
|
height: 48,
|
|
decoration: BoxDecoration(
|
|
color: theme.colorScheme.primaryContainer.withOpacity(0.5),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Icon(
|
|
Icons.notifications_outlined,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class _SettingsTab extends ConsumerWidget {
|
|
final VoidCallback? onReset;
|
|
const _SettingsTab({this.onReset});
|
|
|
|
Widget _buildSettingsTile(BuildContext context, IconData icon, String title,
|
|
{VoidCallback? onTap}) {
|
|
return ListTile(
|
|
leading: Icon(icon,
|
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8)),
|
|
title: Text(
|
|
title,
|
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
trailing: const Icon(Icons.chevron_right, color: AppColors.lightGray),
|
|
onTap: onTap ??
|
|
() {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Settings coming soon!')),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<void> _resetApp(BuildContext context, WidgetRef ref) async {
|
|
final confirmed = await showDialog<bool>(
|
|
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) {
|
|
onReset?.call();
|
|
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
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';
|
|
final isSingle = ref.watch(userProfileProvider
|
|
.select((u) => u?.relationshipStatus == RelationshipStatus.single));
|
|
|
|
return SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Settings',
|
|
style: Theme.of(context).textTheme.displayMedium?.copyWith(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.w600,
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
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)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 60,
|
|
height: 60,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [
|
|
Theme.of(context)
|
|
.colorScheme
|
|
.primary
|
|
.withOpacity(0.7),
|
|
Theme.of(context)
|
|
.colorScheme
|
|
.secondary
|
|
.withOpacity(0.7)
|
|
],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
name.isNotEmpty ? name[0].toUpperCase() : '?',
|
|
style: GoogleFonts.outfit(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
name,
|
|
style:
|
|
Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
Text(
|
|
roleSymbol ?? relationshipStatus,
|
|
style: GoogleFonts.outfit(
|
|
fontSize: 12,
|
|
letterSpacing: 1,
|
|
color: AppColors.warmGray,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const Icon(Icons.chevron_right, color: AppColors.warmGray),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
_buildSettingsGroup(context, 'Preferences', [
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.notifications_outlined,
|
|
'Notifications',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
const NotificationSettingsScreen()));
|
|
},
|
|
),
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.inventory_2_outlined,
|
|
'Period Supplies',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
const SuppliesSettingsScreen()));
|
|
},
|
|
),
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.favorite_outline, // Use a different icon for Relationship
|
|
'Relationship Status',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
const RelationshipSettingsScreen()));
|
|
},
|
|
),
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.flag_outlined,
|
|
'Cycle Goal',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const GoalSettingsScreen()));
|
|
},
|
|
),
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.book_outlined,
|
|
'Bible Version ($translationLabel)',
|
|
onTap: () => BibleUtils.showTranslationPicker(context, ref),
|
|
),
|
|
_buildSettingsTile(context, Icons.palette_outlined, 'Appearance',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const AppearanceScreen()));
|
|
}),
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.favorite_border,
|
|
'My Favorites',
|
|
onTap: () => _showFavoritesDialog(context, ref),
|
|
),
|
|
_buildSettingsTile(context, Icons.security, 'Privacy & Security',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const PrivacySettingsScreen()));
|
|
}),
|
|
if (!isSingle)
|
|
_buildSettingsTile(
|
|
context,
|
|
Icons.share_outlined,
|
|
'Share with Husband',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
const SharingSettingsScreen()));
|
|
},
|
|
),
|
|
]),
|
|
const SizedBox(height: 16),
|
|
_buildSettingsGroup(context, 'Cycle', [
|
|
_buildSettingsTile(
|
|
context, Icons.calendar_today_outlined, 'Cycle Settings',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const CycleSettingsScreen()));
|
|
}),
|
|
_buildSettingsTile(
|
|
context, Icons.trending_up_outlined, 'Cycle History',
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => CycleHistoryScreen()));
|
|
}),
|
|
_buildSettingsTile(
|
|
context, Icons.download_outlined, 'Export Data', onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const ExportDataScreen()));
|
|
}),
|
|
]),
|
|
const SizedBox(height: 16),
|
|
_buildSettingsGroup(context, 'Account', [
|
|
_buildSettingsTile(context, Icons.logout, 'Reset App / Logout',
|
|
onTap: () => _resetApp(context, ref)),
|
|
]),
|
|
const SizedBox(height: 16),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSettingsGroup(
|
|
BuildContext context, String title, List<Widget> tiles) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: GoogleFonts.outfit(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.warmGray,
|
|
letterSpacing: 0.5,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).cardTheme.color,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: Theme.of(context).colorScheme.outline.withOpacity(0.05)),
|
|
),
|
|
child: Column(
|
|
children: tiles,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Future<bool> _authenticate(BuildContext context, String correctPin) async {
|
|
final controller = TextEditingController();
|
|
final pin = await showDialog<String>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Enter PIN'),
|
|
content: TextField(
|
|
controller: controller,
|
|
keyboardType: TextInputType.number,
|
|
obscureText: true,
|
|
maxLength: 4,
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(fontSize: 24, letterSpacing: 8),
|
|
decoration: const InputDecoration(hintText: '....'),
|
|
autofocus: true,
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: const Text('Cancel')),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.pop(context, controller.text),
|
|
child: const Text('Unlock'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return pin == correctPin;
|
|
}
|
|
|
|
void _showFavoritesDialog(BuildContext context, WidgetRef ref) async {
|
|
final userProfile = ref.read(userProfileProvider);
|
|
if (userProfile == null) return;
|
|
|
|
if (userProfile.isBioProtected && userProfile.privacyPin != null) {
|
|
final granted = await _authenticate(context, userProfile.privacyPin!);
|
|
if (!granted) {
|
|
if (context.mounted) {
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(const SnackBar(content: Text('Incorrect PIN')));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
final controller = TextEditingController(
|
|
text: userProfile.favoriteFoods?.join(', ') ?? '',
|
|
);
|
|
|
|
if (!context.mounted) return;
|
|
|
|
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: [
|
|
Icon(Icons.share_outlined,
|
|
color: Theme.of(context).colorScheme.primary),
|
|
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: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color:
|
|
Theme.of(context).colorScheme.primary.withOpacity(0.3)),
|
|
),
|
|
child: SelectableText(
|
|
pairingCode,
|
|
style: GoogleFonts.outfit(
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 4,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
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: Theme.of(context).colorScheme.primary,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('Done'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget _buildWifeTipsSection(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Health Tips',
|
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Card(
|
|
elevation: 2,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildTipCard(
|
|
context,
|
|
title: 'Regular Check-ups',
|
|
content:
|
|
'Schedule regular gynecological check-ups to monitor your reproductive health.',
|
|
icon: Icons.medical_services,
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildTipCard(
|
|
context,
|
|
title: 'Healthy Lifestyle',
|
|
content:
|
|
'Maintain a balanced diet, exercise regularly, and get adequate sleep.',
|
|
icon: Icons.healing,
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildTipCard(
|
|
context,
|
|
title: 'Partner Communication',
|
|
content:
|
|
'Discuss health concerns openly with your partner to ensure mutual understanding.',
|
|
icon: Icons.chat,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildTipCard(
|
|
BuildContext context, {
|
|
required String title,
|
|
required String content,
|
|
required IconData icon,
|
|
}) {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: GoogleFonts.outfit(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 14,
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
content,
|
|
style: GoogleFonts.outfit(
|
|
fontSize: 13,
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|