import 'dart:typed_data'; import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../models/bible_verse.dart'; import '../../models/family_member.dart'; import '../../models/photo.dart'; import '../../models/announcement.dart'; import '../../models/schedule_item.dart'; import '../../services/bible_verse_service.dart'; import '../../services/family_service.dart'; import '../../services/photo_service.dart'; import '../../services/announcement_service.dart'; import '../../services/schedule_service.dart'; class AdminScreen extends StatefulWidget { const AdminScreen({super.key}); @override State createState() => _AdminScreenState(); } class _AdminScreenState extends State { @override Widget build(BuildContext context) { return DefaultTabController( length: 5, child: Scaffold( appBar: AppBar( title: const Text('Admin Settings'), bottom: const TabBar( isScrollable: true, tabs: [ Tab(text: 'Family Members'), Tab(text: 'Photos'), Tab(text: 'Bible Verses'), Tab(text: 'Announcements'), Tab(text: 'Schedules'), ], ), ), body: const TabBarView( children: [ FamilyManagerTab(), PhotoManagerTab(), BibleVerseManagerTab(), AnnouncementManagerTab(), ScheduleManagerTab(), ], ), ), ); } } class FamilyManagerTab extends StatefulWidget { const FamilyManagerTab({super.key}); @override State createState() => _FamilyManagerTabState(); } class _FamilyManagerTabState extends State { // Rainbow palette colors final List _palette = const [ Color(0xFFFF0000), // Red Color(0xFFFF7F00), // Orange Color(0xFFFFFF00), // Yellow Color(0xFF00FF00), // Green Color(0xFF0000FF), // Blue Color(0xFF4B0082), // Indigo Color(0xFF9400D3), // Violet ]; String _colorToHex(Color color) { return '#${color.value.toRadixString(16).padLeft(8, '0').substring(2).toUpperCase()}'; } Color _hexToColor(String hex) { try { String cleanHex = hex.replaceAll('#', '').replaceAll('0x', ''); if (cleanHex.length == 6) { cleanHex = 'FF$cleanHex'; } if (cleanHex.length == 8) { return Color(int.parse(cleanHex, radix: 16)); } return Colors.grey; } catch (_) { return Colors.grey; } } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddMemberDialog(context), child: const Icon(Icons.add), ), body: FutureBuilder>( future: Provider.of(context).fetchFamilyMembers(), builder: (context, snapshot) { if (!snapshot.hasData) return const Center(child: CircularProgressIndicator()); final members = snapshot.data!; return ListView.builder( itemCount: members.length, itemBuilder: (context, index) { final member = members[index]; Color memberColor = _hexToColor(member.color); return ListTile( leading: CircleAvatar( backgroundColor: memberColor, backgroundImage: member.iconUrl.isNotEmpty ? NetworkImage(member.iconUrl) : null, child: member.iconUrl.isEmpty ? Text( member.name.isNotEmpty ? member.name[0].toUpperCase() : '?', style: const TextStyle(fontSize: 20), ) : null, ), title: Text(member.name), subtitle: Text('Order: ${member.order}'), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.edit, color: Colors.blue), onPressed: () => _showEditMemberDialog(context, member), ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteFamilyMember(member.id); setState(() {}); }, ), ], ), ); }, ); }, ), ); } void _showAddMemberDialog(BuildContext context) { final nameController = TextEditingController(); final orderController = TextEditingController(text: '1'); Color selectedColor = _palette[0]; Uint8List? selectedIconBytes; String? selectedIconName; bool isUploading = false; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Family Member'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: nameController, decoration: const InputDecoration(labelText: 'Name'), ), const SizedBox(height: 16), const Text('Icon', style: TextStyle(color: Colors.grey)), const SizedBox(height: 8), Row( children: [ CircleAvatar( backgroundColor: selectedColor, backgroundImage: selectedIconBytes != null ? MemoryImage(selectedIconBytes!) : null, child: selectedIconBytes == null ? const Icon(Icons.person, color: Colors.white) : null, ), const SizedBox(width: 16), ElevatedButton.icon( onPressed: () async { final result = await FilePicker.platform.pickFiles( withData: true, type: FileType.image, ); if (result != null && result.files.isNotEmpty) { setDialogState(() { selectedIconBytes = result.files.first.bytes; selectedIconName = result.files.first.name; }); } }, icon: const Icon(Icons.image), label: const Text('Pick Image'), ), ], ), if (selectedIconName != null) Padding( padding: const EdgeInsets.only(top: 8), child: Text( selectedIconName!, style: const TextStyle(fontSize: 12, color: Colors.grey), ), ), const SizedBox(height: 16), const Text('Color', style: TextStyle(color: Colors.grey)), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: _palette.map((color) { final isSelected = color.value == selectedColor.value; return GestureDetector( onTap: () { setDialogState(() { selectedColor = color; }); }, child: Container( width: 40, height: 40, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: isSelected ? Border.all(color: Colors.white, width: 3) : null, boxShadow: [ if (isSelected) const BoxShadow( color: Colors.black26, blurRadius: 4, spreadRadius: 2, ) ], ), child: isSelected ? const Icon(Icons.check, color: Colors.white) : null, ), ); }).toList(), ), const SizedBox(height: 16), TextField( controller: orderController, decoration: const InputDecoration(labelText: 'Order'), keyboardType: TextInputType.number, ), if (isUploading) const Padding( padding: EdgeInsets.only(top: 16), child: Center(child: CircularProgressIndicator()), ), ], ), ), actions: [ TextButton( onPressed: isUploading ? null : () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: isUploading ? null : () async { if (nameController.text.isNotEmpty) { setDialogState(() => isUploading = true); String iconUrl = ''; if (selectedIconBytes != null && selectedIconName != null) { try { iconUrl = await Provider.of( context, listen: false, ).uploadFamilyIcon( bytes: selectedIconBytes!, filename: selectedIconName!, ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to upload icon: $e')), ); setDialogState(() => isUploading = false); return; } } await Provider.of( context, listen: false, ).createFamilyMember( FamilyMember( id: '', name: nameController.text, emoji: '', // Legacy field iconUrl: iconUrl, color: _colorToHex(selectedColor), order: int.tryParse(orderController.text) ?? 1, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } void _showEditMemberDialog(BuildContext context, FamilyMember member) { final nameController = TextEditingController(text: member.name); final orderController = TextEditingController(text: member.order.toString()); Color selectedColor = _hexToColor(member.color); if (!_palette.any((c) => c.value == selectedColor.value)) { // If current color is not in palette, try to match closest or add it, // but for now let's just default to first palette color if invalid, // or keep it if we want to support custom legacy colors. // The requirement says "replace hex color input with rainbow palette selection". // Let's assume we map to the palette or keep custom if it exists. // But for better UX let's just keep it as is visually, but if they pick a new one it changes. } Uint8List? selectedIconBytes; String? selectedIconName; bool isUploading = false; ImageProvider? previewImage; if (member.iconUrl.isNotEmpty) { previewImage = NetworkImage(member.iconUrl); } showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Edit Family Member'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( controller: nameController, decoration: const InputDecoration(labelText: 'Name'), ), const SizedBox(height: 16), const Text('Icon', style: TextStyle(color: Colors.grey)), const SizedBox(height: 8), Row( children: [ CircleAvatar( backgroundColor: selectedColor, backgroundImage: selectedIconBytes != null ? MemoryImage(selectedIconBytes!) : previewImage, child: (selectedIconBytes == null && previewImage == null) ? Text(member.name.isNotEmpty ? member.name[0] : '?') : null, ), const SizedBox(width: 16), ElevatedButton.icon( onPressed: () async { final result = await FilePicker.platform.pickFiles( withData: true, type: FileType.image, ); if (result != null && result.files.isNotEmpty) { setDialogState(() { selectedIconBytes = result.files.first.bytes; selectedIconName = result.files.first.name; previewImage = selectedIconBytes == null ? previewImage : MemoryImage(selectedIconBytes!); }); } }, icon: const Icon(Icons.image), label: const Text('Change Icon'), ), ], ), const SizedBox(height: 16), const Text('Color', style: TextStyle(color: Colors.grey)), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: _palette.map((color) { final isSelected = color.value == selectedColor.value; return GestureDetector( onTap: () { setDialogState(() { selectedColor = color; }); }, child: Container( width: 40, height: 40, decoration: BoxDecoration( color: color, shape: BoxShape.circle, border: isSelected ? Border.all(color: Colors.white, width: 3) : null, boxShadow: [ if (isSelected) const BoxShadow( color: Colors.black26, blurRadius: 4, spreadRadius: 2, ) ], ), child: isSelected ? const Icon(Icons.check, color: Colors.white) : null, ), ); }).toList(), ), const SizedBox(height: 16), TextField( controller: orderController, decoration: const InputDecoration(labelText: 'Order'), keyboardType: TextInputType.number, ), if (isUploading) const Padding( padding: EdgeInsets.only(top: 16), child: Center(child: CircularProgressIndicator()), ), ], ), ), actions: [ TextButton( onPressed: isUploading ? null : () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: isUploading ? null : () async { if (nameController.text.isNotEmpty) { setDialogState(() => isUploading = true); String iconUrl = member.iconUrl; if (selectedIconBytes != null && selectedIconName != null) { try { iconUrl = await Provider.of( context, listen: false, ).uploadFamilyIcon( bytes: selectedIconBytes!, filename: selectedIconName!, ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to upload icon: $e')), ); setDialogState(() => isUploading = false); return; } } await Provider.of( context, listen: false, ).updateFamilyMember( FamilyMember( id: member.id, name: nameController.text, emoji: member .emoji, // Keep existing emoji or clear it? Let's keep it to be safe iconUrl: iconUrl, color: _colorToHex(selectedColor), order: int.tryParse(orderController.text) ?? 1, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Save'), ), ], ), ), ); } } class PhotoManagerTab extends StatefulWidget { const PhotoManagerTab({super.key}); @override State createState() => _PhotoManagerTabState(); } class _PhotoManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddPhotoDialog(context), child: const Icon(Icons.add_a_photo), ), body: FutureBuilder>( future: Provider.of(context).fetchPhotos(), builder: (context, snapshot) { if (!snapshot.hasData) return const Center(child: CircularProgressIndicator()); final photos = snapshot.data!; return GridView.builder( padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: photos.length, itemBuilder: (context, index) { final photo = photos[index]; return GridTile( footer: GridTileBar( backgroundColor: Colors.black54, title: Text(photo.caption), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.white), onPressed: () async { await Provider.of( context, listen: false, ).deletePhoto(photo.id); setState(() {}); }, ), ), child: Image.network( photo.url, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Center(child: Icon(Icons.broken_image)), ), ); }, ); }, ), ); } void _showAddPhotoDialog(BuildContext context) { final urlController = TextEditingController(); final captionController = TextEditingController(); Uint8List? selectedFileBytes; String? selectedFileName; bool isUploading = false; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Photo'), content: Column( mainAxisSize: MainAxisSize.min, children: [ ElevatedButton.icon( onPressed: () async { final result = await FilePicker.platform.pickFiles( withData: true, type: FileType.image, ); if (result != null && result.files.isNotEmpty) { setDialogState(() { selectedFileBytes = result.files.first.bytes; selectedFileName = result.files.first.name; // Clear URL if file is selected to avoid confusion urlController.clear(); }); } }, icon: const Icon(Icons.upload_file), label: const Text('Pick Local Image'), ), if (selectedFileName != null) Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Text( 'Selected: $selectedFileName', style: const TextStyle(fontWeight: FontWeight.bold), ), ), const Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text('- OR -', style: TextStyle(color: Colors.grey)), ), TextField( controller: urlController, decoration: const InputDecoration(labelText: 'Image URL'), enabled: selectedFileBytes == null, ), TextField( controller: captionController, decoration: const InputDecoration(labelText: 'Caption'), ), if (isUploading) const Padding( padding: EdgeInsets.only(top: 16), child: CircularProgressIndicator(), ), ], ), actions: [ TextButton( onPressed: isUploading ? null : () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: isUploading ? null : () async { if (selectedFileBytes != null || urlController.text.isNotEmpty) { setDialogState(() { isUploading = true; }); try { final photoService = Provider.of(context, listen: false); if (selectedFileBytes != null) { await photoService.uploadPhotoBytes( bytes: selectedFileBytes!, filename: selectedFileName!, caption: captionController.text, ); } else { await photoService.createPhoto( Photo( id: '', url: urlController.text, caption: captionController.text, active: true, ), ); } if (mounted) { Navigator.pop(context); setState(() {}); } } catch (e) { setDialogState(() { isUploading = false; }); // Optionally show error if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: $e')), ); } } } }, child: const Text('Add'), ), ], ), ), ); } } class BibleVerseManagerTab extends StatefulWidget { const BibleVerseManagerTab({super.key}); @override State createState() => _BibleVerseManagerTabState(); } class _BibleVerseManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddVerseDialog(context), child: const Icon(Icons.menu_book), ), body: FutureBuilder>( future: Provider.of(context).fetchVerses(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final verses = snapshot.data!; if (verses.isEmpty) { return const Center( child: Text( 'No verses added yet', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( itemCount: verses.length, itemBuilder: (context, index) { final verse = verses[index]; return ListTile( title: Text(verse.reference), subtitle: Text( verse.text, maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (verse.date != null && verse.date!.isNotEmpty) Text( verse.date!, style: const TextStyle(fontSize: 12, color: Colors.grey), ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteVerse(verse.id); setState(() {}); }, ), ], ), ); }, ); }, ), ); } void _showAddVerseDialog(BuildContext context) { final textController = TextEditingController(); final referenceController = TextEditingController(); final dateController = TextEditingController(); bool isActive = true; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Bible Verse'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: referenceController, decoration: const InputDecoration( labelText: 'Reference (e.g., Psalms 23:1)', ), ), const SizedBox(height: 8), TextField( controller: textController, decoration: const InputDecoration( labelText: 'Verse Text (Korean)', ), maxLines: 3, ), const SizedBox(height: 8), TextField( controller: dateController, decoration: const InputDecoration( labelText: 'Date (YYYY-MM-DD) - Optional', hintText: '2024-01-01', ), ), const SizedBox(height: 8), SwitchListTile( title: const Text('Active'), value: isActive, onChanged: (value) { setDialogState(() { isActive = value; }); }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (textController.text.isNotEmpty && referenceController.text.isNotEmpty) { await Provider.of( context, listen: false, ).createVerse( BibleVerse( id: '', text: textController.text, reference: referenceController.text, date: dateController.text.isEmpty ? null : dateController.text, active: isActive, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } } class AnnouncementManagerTab extends StatefulWidget { const AnnouncementManagerTab({super.key}); @override State createState() => _AnnouncementManagerTabState(); } class _AnnouncementManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddAnnouncementDialog(context), child: const Icon(Icons.campaign), ), body: FutureBuilder>( future: Provider.of(context).fetchAnnouncements(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final announcements = snapshot.data!; if (announcements.isEmpty) { return const Center( child: Text( 'No announcements added yet', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( itemCount: announcements.length, itemBuilder: (context, index) { final announcement = announcements[index]; return ListTile( title: Text(announcement.title), subtitle: Text( announcement.content, maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (announcement.priority > 0) Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.orangeAccent, borderRadius: BorderRadius.circular(4), ), child: Text( 'P${announcement.priority}', style: const TextStyle( color: Colors.white, fontSize: 10), ), ), const SizedBox(width: 8), Icon( announcement.active ? Icons.check_circle : Icons.cancel, color: announcement.active ? Colors.green : Colors.grey, size: 16, ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteAnnouncement(announcement.id); setState(() {}); }, ), ], ), ); }, ); }, ), ); } void _showAddAnnouncementDialog(BuildContext context) { final titleController = TextEditingController(); final contentController = TextEditingController(); final priorityController = TextEditingController(text: '0'); bool isActive = true; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Announcement'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: titleController, decoration: const InputDecoration(labelText: 'Title'), ), TextField( controller: contentController, decoration: const InputDecoration(labelText: 'Content'), maxLines: 3, ), TextField( controller: priorityController, decoration: const InputDecoration(labelText: 'Priority (0-10)'), keyboardType: TextInputType.number, ), SwitchListTile( title: const Text('Active'), value: isActive, onChanged: (value) { setDialogState(() { isActive = value; }); }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (titleController.text.isNotEmpty) { await Provider.of( context, listen: false, ).createAnnouncement( Announcement( id: '', title: titleController.text, content: contentController.text, priority: int.tryParse(priorityController.text) ?? 0, active: isActive, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } } class ScheduleManagerTab extends StatefulWidget { const ScheduleManagerTab({super.key}); @override State createState() => _ScheduleManagerTabState(); } class _ScheduleManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddScheduleDialog(context), child: const Icon(Icons.calendar_today), ), body: FutureBuilder>( future: Provider.of(context).fetchSchedules(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final schedules = snapshot.data!; if (schedules.isEmpty) { return const Center( child: Text( 'No schedules added yet', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( itemCount: schedules.length, itemBuilder: (context, index) { final schedule = schedules[index]; return ListTile( title: Text(schedule.title), subtitle: Text( '${schedule.description}\n${schedule.startDate.toIso8601String().split('T')[0]} ~ ${schedule.endDate.toIso8601String().split('T')[0]}', maxLines: 2, overflow: TextOverflow.ellipsis, ), isThreeLine: true, trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteSchedule(schedule.id); setState(() {}); }, ), ); }, ); }, ), ); } void _showAddScheduleDialog(BuildContext context) { final titleController = TextEditingController(); final descriptionController = TextEditingController(); final startController = TextEditingController( text: DateTime.now().toIso8601String().split('T')[0], ); final endController = TextEditingController( text: DateTime.now().toIso8601String().split('T')[0], ); bool isAllDay = true; String? selectedFamilyMemberId; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Schedule'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: titleController, decoration: const InputDecoration(labelText: 'Title'), ), TextField( controller: descriptionController, decoration: const InputDecoration(labelText: 'Description'), ), TextField( controller: startController, decoration: const InputDecoration( labelText: 'Start Date (YYYY-MM-DD)', ), ), TextField( controller: endController, decoration: const InputDecoration( labelText: 'End Date (YYYY-MM-DD)', ), ), SwitchListTile( title: const Text('All Day'), value: isAllDay, onChanged: (value) { setDialogState(() { isAllDay = value; }); }, ), FutureBuilder>( future: Provider.of(context, listen: false) .fetchFamilyMembers(), builder: (context, snapshot) { if (!snapshot.hasData) return const SizedBox(); final members = snapshot.data!; return DropdownButtonFormField( value: selectedFamilyMemberId, decoration: const InputDecoration( labelText: 'Family Member', ), items: members.map((member) { return DropdownMenuItem( value: member.id, child: Text(member.name), ); }).toList(), onChanged: (value) { setDialogState(() { selectedFamilyMemberId = value; }); }, ); }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (titleController.text.isNotEmpty) { final startDate = DateTime.tryParse(startController.text) ?? DateTime.now(); final endDate = DateTime.tryParse(endController.text) ?? DateTime.now(); await Provider.of( context, listen: false, ).createSchedule( ScheduleItem( id: '', title: titleController.text, description: descriptionController.text, startDate: startDate, endDate: endDate, familyMemberId: selectedFamilyMemberId ?? '', isAllDay: isAllDay, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } }