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:
2025-12-26 22:40:52 -06:00
parent 464692ce56
commit b4b2bfe749
47 changed files with 240110 additions and 2578 deletions

View 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),
],
);
}
}