Implement dynamic scripture loading from XML and fix theming for Learn screens

This commit is contained in:
2026-01-05 17:40:48 -06:00
parent 96655f9a74
commit d50cab3188
2 changed files with 119 additions and 29 deletions

View File

@@ -19,7 +19,7 @@ class _HusbandLearnScreen extends StatelessWidget {
style: GoogleFonts.outfit( style: GoogleFonts.outfit(
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.navyBlue, color: Theme.of(context).textTheme.displayMedium?.color,
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
@@ -94,14 +94,14 @@ class _HusbandLearnScreen extends StatelessWidget {
style: GoogleFonts.outfit( style: GoogleFonts.outfit(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: AppColors.warmGray, color: Theme.of(context).textTheme.bodySmall?.color,
letterSpacing: 0.5, letterSpacing: 0.5,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).cardTheme.color,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
@@ -111,12 +111,12 @@ class _HusbandLearnScreen extends StatelessWidget {
width: 40, width: 40,
height: 40, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.navyBlue.withOpacity(0.1), color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: Icon( child: Icon(
item.icon, item.icon,
color: AppColors.navyBlue, color: Theme.of(context).colorScheme.primary,
size: 20, size: 20,
), ),
), ),
@@ -125,19 +125,19 @@ class _HusbandLearnScreen extends StatelessWidget {
style: GoogleFonts.outfit( style: GoogleFonts.outfit(
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: AppColors.charcoal, color: Theme.of(context).textTheme.bodyLarge?.color,
), ),
), ),
subtitle: Text( subtitle: Text(
item.subtitle, item.subtitle,
style: GoogleFonts.outfit( style: GoogleFonts.outfit(
fontSize: 13, fontSize: 13,
color: AppColors.warmGray, color: Theme.of(context).textTheme.bodyMedium?.color,
), ),
), ),
trailing: const Icon( trailing: Icon(
Icons.chevron_right, Icons.chevron_right,
color: AppColors.lightGray, color: Theme.of(context).disabledColor,
), ),
onTap: () { onTap: () {
Navigator.push( Navigator.push(

View File

@@ -6,6 +6,7 @@ import '../../models/user_profile.dart';
import '../../models/teaching_plan.dart'; import '../../models/teaching_plan.dart';
import '../../providers/user_provider.dart'; import '../../providers/user_provider.dart';
import '../../theme/app_theme.dart'; import '../../theme/app_theme.dart';
import '../../services/bible_xml_parser.dart';
class HusbandDevotionalScreen extends ConsumerStatefulWidget { class HusbandDevotionalScreen extends ConsumerStatefulWidget {
const HusbandDevotionalScreen({super.key}); const HusbandDevotionalScreen({super.key});
@@ -15,6 +16,61 @@ class HusbandDevotionalScreen extends ConsumerStatefulWidget {
} }
class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScreen> { class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScreen> {
final _parser = BibleXmlParser();
Map<String, String> _scriptures = {};
bool _loading = true;
BibleTranslation? _currentTranslation;
@override
void initState() {
super.initState();
// Initial fetch handled in post-frame or via listen, but let's trigger once here if possible
// We need the ref, which is available.
WidgetsBinding.instance.addPostFrameCallback((_) {
_fetchScriptures();
});
}
Future<void> _fetchScriptures() async {
final user = ref.read(userProfileProvider);
if (user == null) return;
final translation = user.bibleTranslation;
if (translation == _currentTranslation && _scriptures.isNotEmpty) return;
setState(() => _loading = true);
try {
final assetPath = 'assets/bible_xml/${translation.name.toUpperCase()}.xml';
// Define verses to fetch
final versesToFetch = [
'1 Corinthians 11:3',
'1 Timothy 3:4',
'1 Timothy 3:5',
'1 Timothy 3:12',
'Titus 1:6',
];
final Map<String, String> results = {};
for (final ref in versesToFetch) {
final text = await _parser.getVerseFromAsset(assetPath, ref);
results[ref] = text ?? 'Verse not found.';
}
if (mounted) {
setState(() {
_scriptures = results;
_currentTranslation = translation;
_loading = false;
});
}
} catch (e) {
debugPrint('Error loading scriptures: $e');
if (mounted) setState(() => _loading = false);
}
}
void _showAddTeachingDialog([TeachingPlan? existingPlan]) { void _showAddTeachingDialog([TeachingPlan? existingPlan]) {
final titleController = TextEditingController(text: existingPlan?.topic); final titleController = TextEditingController(text: existingPlan?.topic);
@@ -163,6 +219,13 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
final upcomingPlans = user?.teachingPlans ?? []; final upcomingPlans = user?.teachingPlans ?? [];
upcomingPlans.sort((a,b) => a.date.compareTo(b.date)); upcomingPlans.sort((a,b) => a.date.compareTo(b.date));
// Listen for translation changes to re-fetch
ref.listen(userProfileProvider, (prev, next) {
if (next?.bibleTranslation != prev?.bibleTranslation) {
_fetchScriptures();
}
});
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Spiritual Leadership'), title: const Text('Spiritual Leadership'),
@@ -174,7 +237,7 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Informational Card (Headship) // Informational Card (Headship)
_buildHeadshipCard(), _buildHeadshipCard(user?.bibleTranslation.label ?? 'ESV'),
const SizedBox(height: 24), const SizedBox(height: 24),
Row( Row(
@@ -289,7 +352,15 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
); );
} }
Widget _buildHeadshipCard() { Widget _buildHeadshipCard(String version) {
// Combine 1 Timothy verses
String timothyText = 'Loading...';
if (!_loading) {
timothyText = '${_scriptures['1 Timothy 3:4'] ?? '...'} ${_scriptures['1 Timothy 3:5'] ?? ''} ... ${_scriptures['1 Timothy 3:12'] ?? ''}';
// Cleanup potential double spaces or missing
timothyText = timothyText.replaceAll(' ', ' ').trim();
}
return Container( return Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -304,6 +375,9 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
children: [ children: [
const Icon(Icons.menu_book, color: Color(0xFF8B5E3C)), const Icon(Icons.menu_book, color: Color(0xFF8B5E3C)),
const SizedBox(width: 12), const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Biblical Principles', 'Biblical Principles',
style: GoogleFonts.lora( style: GoogleFonts.lora(
@@ -312,20 +386,32 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
color: const Color(0xFF5D4037), color: const Color(0xFF5D4037),
), ),
), ),
Text(
version,
style: GoogleFonts.outfit(fontSize: 12, color: const Color(0xFF8B5E3C)),
),
],
),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildVerseText( _buildVerseText(
'1 Corinthians 11:3', '1 Corinthians 11:3',
'“The head of every man is Christ, the head of a wife is her husband, and the head of Christ is God.', _loading ? 'Loading...' : (_scriptures['1 Corinthians 11:3'] ?? 'Verse not found.'),
'Supports family structure under Christs authority.', 'Supports family structure under Christs authority.',
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider(height: 1, color: Color(0xFFE0C097)), const Divider(height: 1, color: Color(0xFFE0C097)),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildVerseText( _buildVerseText(
'1 Tim 3:45, 12 & Titus 1:6', '1 Timothy 3:45, 12',
timothyText,
'Qualifications for church elders include managing their own households well.', 'Qualifications for church elders include managing their own households well.',
),
const SizedBox(height: 16),
_buildVerseText(
'Titus 1:6',
_loading ? 'Loading...' : (_scriptures['Titus 1:6'] ?? 'Verse not found.'),
'Husbands who lead faithfully at home are seen as candidates for formal spiritual leadership.', 'Husbands who lead faithfully at home are seen as candidates for formal spiritual leadership.',
), ),
], ],
@@ -346,8 +432,11 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Text(
text, text,
key: ValueKey(text), // Animate change
style: GoogleFonts.lora( style: GoogleFonts.lora(
fontSize: 15, fontSize: 15,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
@@ -355,6 +444,7 @@ class _HusbandDevotionalScreenState extends ConsumerState<HusbandDevotionalScree
color: const Color(0xFF3E2723), color: const Color(0xFF3E2723),
), ),
), ),
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
context, context,