So, i’ve developed a calendar in flutter, it ought to load within the chosen days three informations: drugs, occasions and meals. In debug mode it really works simply wonderful, however in manufacturing when i attempt to entry the calendar, if it have any drugs registered, the display screen freezes and no data is loaded. Each apple and google manufacturing variations make this. Any suggestions?
import 'bundle:app_cuidadoria/no_bloc_components/add_event_item.dart';
import 'bundle:app_cuidadoria/no_bloc_models/occasion.dart';
import 'bundle:app_cuidadoria/no_bloc_models/refeicao.dart';
import 'bundle:app_cuidadoria/shared_components/custom_hour_field.dart';
import 'bundle:app_cuidadoria/side_menu/pages/side_menu_drawer.dart';
import 'bundle:cloud_firestore/cloud_firestore.dart';
import 'bundle:flutter/materials.dart';
import 'bundle:flutter/companies.dart';
import 'bundle:intl/intl.dart';
import 'bundle:shared_preferences/shared_preferences.dart';
import 'bundle:table_calendar/table_calendar.dart';
import 'bundle:app_cuidadoria/no_bloc_components/add_medicine_item.dart';
import 'bundle:app_cuidadoria/no_bloc_components/event_item.dart';
import 'bundle:app_cuidadoria/no_bloc_models/drugs.dart';
import 'bundle:uuid/uuid.dart';
class MedicineCalendarPage extends StatefulWidget {
const MedicineCalendarPage({
tremendous.key,
required this.uid,
});
remaining String uid;
@override
State createState() => _MedicineCalendarPageState();
}
class _MedicineCalendarPageState extends State {
DateTime _selectedDate =
DateTime(DateTime.now().12 months, DateTime.now().month, DateTime.now().day);
Checklist _eventsForSelectedDay = [];
Checklist medicineList = [];
Checklist eventList = [];
Checklist mealList = [];
Checklist _selectedDays = [];
int userLevel = 0;
@override
void initState() {
init();
tremendous.initState();
}
init() async {
remaining cache = await SharedPreferences.getInstance();
await _loadEventsForSelectedDate(_selectedDate);
await _loadEventsNotMedForSelectedDate(_selectedDate);
await _loadMealsForSelectedDate(_selectedDate);
setState(() {
userLevel = cache.getInt('userLevel') ?? 0;
});
_eventsForSelectedDay.type((a, b) {
// Expressão common para capturar HH:mm no remaining da string
RegExp regExp = RegExp(r'(d{2}):(d{2})$');
Match? matchA = regExp.firstMatch(a);
Match? matchB = regExp.firstMatch(b);
if (matchA != null && matchB != null) {
int hourA = int.parse(matchA.group(1)!);
int minuteA = int.parse(matchA.group(2)!);
int hourB = int.parse(matchB.group(1)!);
int minuteB = int.parse(matchB.group(2)!);
// Criamos uma representação em minutos do dia para comparar
int totalMinutesA = hourA * 60 + minuteA;
int totalMinutesB = hourB * 60 + minuteB;
return totalMinutesA.compareTo(totalMinutesB);
}
return 0; // Caso não tenha horário, não altera a ordem
});
}
Future _loadEventsForSelectedDate(DateTime selectedDate) async {
selectedDate =
DateTime(selectedDate.12 months, selectedDate.month, selectedDate.day);
remaining querySnapshot = await FirebaseFirestore.occasion
.assortment('medicines')
.the place('uid', isEqualTo: widget.uid)
.get();
Checklist loadedEvents = [];
for (var doc in querySnapshot.docs) {
remaining knowledge = doc.knowledge();
DateTime startDate =
DateTime.fromMillisecondsSinceEpoch(knowledge['startDate'] as int);
DateTime adjustedStartDate =
DateTime(startDate.12 months, startDate.month, startDate.day);
DateTime? endDate = knowledge['endDate'] != null
? DateTime.fromMillisecondsSinceEpoch(knowledge['endDate'] as int)
: null;
String description = knowledge['medicineName'];
bool repeatDaily = knowledge['repeatDaily'];
bool repeatMultipleTimes = knowledge['repeatMultipleTimes'];
int? hourInterval = knowledge['hourInterval'];
int? dayInterval = knowledge['dayInterval'];
int? dosage = (knowledge['dosage'] as double).spherical();
String? sort = knowledge['type'];
Checklist? specificDays = knowledge['specificDays'] != null
? Checklist.from((knowledge['specificDays'] as Checklist))
: null;
if (selectedDate.isBefore(adjustedStartDate) ||
(endDate != null && selectedDate.isAfter(endDate))) {
proceed; // Ignora eventos fora do intervalo de datas válidas
}
// Verifica se o medicamento deve ser tomado no dia selecionado
bool shouldGenerateEvent = false;
Checklist convertedSpecificDays = [];
if (specificDays != null && specificDays.isNotEmpty) {
convertedSpecificDays = specificDays
.map((e) {
change (e.toLowerCase()) {
case 'seg':
return DateTime.monday;
case 'ter':
return DateTime.tuesday;
case 'qua':
return DateTime.wednesday;
case 'qui':
return DateTime.thursday;
case 'intercourse':
return DateTime.friday;
case 'sab':
return DateTime.saturday;
case 'dom':
return DateTime.sunday;
default:
return -1; // Valor inválido
}
})
.the place((day) => day != -1)
.toList(); // Filtra dias inválidos
}
if (repeatDaily) {
shouldGenerateEvent = true;
} else if (dayInterval != null &&
// selectedDate.distinction(adjustedStartDate).inDays % dayInterval == 0
(selectedDate.day == adjustedStartDate.day ||
(selectedDate.day - adjustedStartDate.day) % dayInterval == 0)) {
shouldGenerateEvent = true;
} else if (convertedSpecificDays.comprises(selectedDate.weekday)) {
shouldGenerateEvent = true;
}
if (shouldGenerateEvent) {
// Calcula a primeira dose do dia baseado no horário mais próximo
DateTime firstDose = DateTime(
selectedDate.12 months,
selectedDate.month,
selectedDate.day,
startDate.hour,
startDate.minute,
);
loadedEvents.add(
'$description:n$dosage $sort - ${DateFormat('HH:mm').format(firstDose)}');
medicineList.add(Drugs.fromDocument(doc));
// Se houver múltiplas doses no mesmo dia, calcula os próximos horários
if (repeatMultipleTimes && hourInterval != null) {
DateTime nextDose = firstDose.add(Length(hours: hourInterval));
whereas (nextDose.day == selectedDate.day) {
loadedEvents.add(
'$description:n$dosage $sort - ${DateFormat('HH:mm').format(nextDose)}');
nextDose = nextDose.add(Length(hours: hourInterval));
medicineList.add(Drugs.fromDocument(doc));
}
}
}
}
// setState(() {
_eventsForSelectedDay = loadedEvents;
// });
}
Future _loadMealsForSelectedDate(DateTime selectedDate) async {
remaining querySnapshot = await FirebaseFirestore.occasion
.assortment('meals')
.the place('idPaciente', isEqualTo: widget.uid)
.get();
Set uniqueMealIds = {}; // Para rastrear IDs únicos
Checklist loadedMeals = [];
selectedDate = DateTime(selectedDate.12 months, selectedDate.month, selectedDate.day);
for (var doc in querySnapshot.docs) {
remaining knowledge = doc.knowledge();
String description = '${knowledge['titulo']} - ${knowledge['horario']}';
String id = knowledge['id'];
if (uniqueMealIds.comprises(id)) proceed; // Evita duplicatas
Checklist? specificDays =
knowledge['dia'] != null ? Checklist.from((knowledge['dia'] as Checklist)) : null;
// Verifica se o medicamento deve ser tomado no dia selecionado
bool shouldGenerateEvent = false;
Checklist convertedSpecificDays = [];
if (specificDays != null && specificDays.isNotEmpty) {
convertedSpecificDays = specificDays
.map((e) {
change (e.toLowerCase()) {
case 'seg': return DateTime.monday;
case 'ter': return DateTime.tuesday;
case 'qua': return DateTime.wednesday;
case 'qui': return DateTime.thursday;
case 'intercourse': return DateTime.friday;
case 'sáb': return DateTime.saturday;
case 'dom': return DateTime.sunday;
default: return -1; // Valor inválido
}
})
.the place((day) => day != -1)
.toList(); // Filtra dias inválidos
}
if (convertedSpecificDays.comprises(selectedDate.weekday)) {
shouldGenerateEvent = true;
}
if (shouldGenerateEvent) {
uniqueMealIds.add(id); // Marca o ID como adicionado
loadedMeals.add(description);
mealList.add(Refeicao.fromDocument(doc));
}
}
// Atualiza o estado apenas uma vez
// setState(() {
_eventsForSelectedDay.addAll(loadedMeals);
// });
}
Future _loadEventsNotMedForSelectedDate(DateTime selectedDate) async {
remaining querySnapshot = await FirebaseFirestore.occasion
.assortment('occasions')
.the place('uid', isEqualTo: widget.uid)
.get();
Checklist loadedEvents = [];
selectedDate =
DateTime(selectedDate.12 months, selectedDate.month, selectedDate.day);
for (var doc in querySnapshot.docs) {
remaining knowledge = doc.knowledge();
DateTime startDate =
DateTime.fromMillisecondsSinceEpoch(knowledge['startDate'] as int);
DateTime adjustedStartDate =
DateTime(startDate.12 months, startDate.month, startDate.day);
DateTime? endDate = knowledge['endDate'] != null
? DateTime.fromMillisecondsSinceEpoch(knowledge['endDate'] as int)
: null;
String description = knowledge['description'];
bool repeatDaily = knowledge['repeatDaily'];
bool repeatMultipleTimes = knowledge['repeatMultipleTimes'];
int? hourInterval = knowledge['hourInterval'];
int? dayInterval = knowledge['dayInterval'];
Checklist? specificDays = knowledge['specificDays'] != null
? Checklist.from((knowledge['specificDays'] as Checklist))
: null;
if (selectedDate.isBefore(adjustedStartDate) ||
(endDate != null && selectedDate.isAfter(endDate))) {
proceed;
}
bool shouldGenerateEvent = false;
Checklist convertedSpecificDays = [];
if (specificDays != null && specificDays.isNotEmpty) {
convertedSpecificDays = specificDays
.map((e) {
change (e.toLowerCase()) {
case 'seg':
return DateTime.monday;
case 'ter':
return DateTime.tuesday;
case 'qua':
return DateTime.wednesday;
case 'qui':
return DateTime.thursday;
case 'intercourse':
return DateTime.friday;
case 'sáb':
return DateTime.saturday;
case 'dom':
return DateTime.sunday;
default:
return -1;
}
})
.the place((day) => day != -1)
.toList();
}
if (repeatDaily) {
shouldGenerateEvent = true;
} else if (dayInterval != null &&
(selectedDate.day == adjustedStartDate.day ||
(selectedDate.day - adjustedStartDate.day) % dayInterval == 0)) {
shouldGenerateEvent = true;
} else if (convertedSpecificDays.comprises(selectedDate.weekday)) {
shouldGenerateEvent = true;
}
if (shouldGenerateEvent) {
DateTime firstDose = DateTime(
selectedDate.12 months,
selectedDate.month,
selectedDate.day,
startDate.hour,
startDate.minute,
);
loadedEvents
.add('$description - ${DateFormat('HH:mm').format(firstDose)}');
eventList.add(Occasion.fromDocument(doc));
if (repeatMultipleTimes && hourInterval != null) {
DateTime nextDose = firstDose.add(Length(hours: hourInterval));
whereas (nextDose.day == selectedDate.day) {
loadedEvents
.add('$description - ${DateFormat('HH:mm').format(nextDose)}');
nextDose = nextDose.add(Length(hours: hourInterval));
eventList.add(Occasion.fromDocument(doc));
}
}
}
}
setState(() {
_eventsForSelectedDay.addAll(loadedEvents);
_eventsForSelectedDay.type((a, b) {
// Expressão common para capturar HH:mm no remaining da string
RegExp regExp = RegExp(r'(d{2}):(d{2})$');
Match? matchA = regExp.firstMatch(a);
Match? matchB = regExp.firstMatch(b);
if (matchA != null && matchB != null) {
int hourA = int.parse(matchA.group(1)!);
int minuteA = int.parse(matchA.group(2)!);
int hourB = int.parse(matchB.group(1)!);
int minuteB = int.parse(matchB.group(2)!);
// Criamos uma representação em minutos do dia para comparar
int totalMinutesA = hourA * 60 + minuteA;
int totalMinutesB = hourB * 60 + minuteB;
return totalMinutesA.compareTo(totalMinutesB);
}
return 0; // Caso não tenha horário, não altera a ordem
});
});
}
void showAlertDialog() async {
remaining alertDialog = AlertDialog(
content material: AddMedicineItem(
uid: widget.uid,
),
);
showDialog(
context: context,
builder: (context) {
return alertDialog;
},
);
}
void showAlertDialogEvent() async {
var alertDialog = AlertDialog(
content material: AddEventItem(uid: widget.uid,),
);
showDialog(
context: context,
builder: (context) {
return alertDialog;
},
);
}
void showMealAlertDialog() {
remaining TextEditingController tituloController = TextEditingController();
remaining TextEditingController horarioController = TextEditingController();
// Lista de dias da semana
// Lista para armazenar os dias selecionados
Checklist selectedDias = [];
String loggedUid = '';
const Uuid uuid = Uuid();
// Função para buscar as refeições do paciente no Firestore
Future addMeal(Refeicao newMeal) async {
await FirebaseFirestore.occasion.assortment('meals').add(newMeal.toMap());
}
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, alertState) {
return AlertDialog(
form: RoundedRectangleBorder(
borderRadius: BorderRadius.round(12),
),
contentPadding: const EdgeInsets.all(16),
content material: SingleChildScrollView(
// Para garantir que o conteúdo seja rolável
little one: Column(
crossAxisAlignment: CrossAxisAlignment.begin,
mainAxisSize:
MainAxisSize.min, // Ajusta para o tamanho mínimo necessário
youngsters: [
// Usando FutureBuilder para aguardar a carga das refeições
const SizedBox(height: 20),
// Formulário para cadastro de nova refeição
const Text(
"Cadastrar nova refeição",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
const SizedBox(height: 10),
TextField(
controller: tituloController,
decoration: InputDecoration(
labelText: 'Nome da Refeição',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 10),
TextField(
controller: horarioController,
decoration: InputDecoration(
labelText: 'Horário (HH:mm)',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter
.digitsOnly, // Permite apenas números
TimeInputFormatter(), // Formata como `HH:mm`
],
),
const SizedBox(peak: 10),
// Adicionando os dias da semana com AnimatedDropdownSearch
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
little one: Column(
crossAxisAlignment: CrossAxisAlignment.begin,
youngsters: [
const Text("Selecione os dias da semana:"),
Wrap(
spacing: 8.0,
children:
['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']
.map((day) => ChoiceChip(
label: Textual content(day),
chosen: _selectedDays.comprises(day),
onSelected: (chosen) {
alertState(() {
if (chosen) {
_selectedDays.add(day);
} else {
_selectedDays.take away(day);
}
});
},
))
.toList(),
),
],
),
),
const SizedBox(peak: 20),
ElevatedButton(
onPressed: () async {
remaining newMeal = Refeicao(
id: uuid.v4(),
idFuncionario: loggedUid,
idPaciente: widget.uid,
titulo: tituloController.textual content,
dia: _selectedDays, // Dias da semana selecionados
horario: horarioController.textual content,
timestamp: DateTime.now(),
);
await addMeal(newMeal);
Navigator.pop(context);
},
type: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
vertical: 12, horizontal: 20),
form: RoundedRectangleBorder(
borderRadius: BorderRadius.round(8),
),
),
little one: const Textual content(
'Registrar',
type: TextStyle(fontSize: 16),
),
),
],
),
),
);
});
},
);
}
void showOptionsAlertDialog() async {
remaining alertDialog = AlertDialog(
content material: Column(
mainAxisSize: MainAxisSize.min,
youngsters: [
InkWell(
onTap: () {
showAlertDialog();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.purple.shade300,
),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Adicionar Medicamento',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
),
),
),
InkWell(
onTap: () {
showAlertDialogEvent();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.purple.shade300,
),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Text(
'Adicionar Evento',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
),
),
),
),
InkWell(
onTap: () {
showMealAlertDialog();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.purple.shade300,
),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Text(
'Adicionar Refeição',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
),
),
),
),
],
),
);
showDialog(
context: context,
builder: (context) {
return alertDialog;
},
);
}
@override
Widget construct(BuildContext context) {
return Scaffold(
drawer: const SideMenuDrawer(),
appBar: AppBar(
title: const Textual content('Agenda do Paciente'),
actions: [
if (userLevel == 4 || userLevel == 1)
IconButton(
icon: const Icon(Icons.add),
onPressed: () async {
showOptionsAlertDialog();
},
)
],
),
physique: Column(
youngsters: [
TableCalendar(
locale: 'pt-BR',
firstDay: DateTime.now(),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _selectedDate,
selectedDayPredicate: (day) => isSameDay(_selectedDate, day),
onDaySelected: (selectedDay, focusedDay) async {
setState(() {
_selectedDate = selectedDay;
});
_eventsForSelectedDay.clear();
await _loadEventsForSelectedDate(selectedDay);
await _loadMealsForSelectedDate(selectedDay);
await _loadEventsNotMedForSelectedDate(selectedDay);
},
),
const SizedBox(height: 8),
Expanded(
child: _eventsForSelectedDay.isEmpty
? const Center(child: Text('Nenhum evento para este dia'))
: ListView.builder(
itemCount: _eventsForSelectedDay.length,
itemBuilder: (context, index) {
Medicine? medicineSelected;
Event? eventSelected;
Refeicao? mealSelected;
try {
setState(() {
medicineSelected = medicineList.firstWhere(
(element) =>
element.medicineName ==
_eventsForSelectedDay[index].break up(':n')[0],
);
});
} catch (e) {
strive {
if (!_eventsForSelectedDay[index].comprises(':n')) {
// setState(() {
eventSelected = eventList.firstWhere(
(ingredient) =>
ingredient.description ==
_eventsForSelectedDay[index]
.break up(RegExp(r' - d{2}:d{2}'))[0],
);
// });
}
} catch (e) {
if (!_eventsForSelectedDay[index].comprises(':n')) {
// setState(() {
mealSelected = mealList.firstWhere(
(ingredient) =>
ingredient.titulo ==
_eventsForSelectedDay[index]
.break up(RegExp(r' - d{2}:d{2}'))[0],
);
// });
}
}
}
return EventItem(
title: _eventsForSelectedDay[index],
drugs: medicineSelected,
occasion: eventSelected,
refeicao: mealSelected,
selectedDate: _selectedDate,
);
},
),
),
],
),
);
}
}