im build an app and storing a bunch of data in the shared preferences.
I’m storing several studentsLists and photos in base64, the same way, with the list followed by the date of save, like this
String? savedPhotoString = prefs.getString(‘savedPhoto_$formattedDate’);
The problem is, im building a calendar page to show the data of the day, but when i navigate through the days with events, it dont update the list, only when i click on a day without event and again on other with events…
When I select another day with events, the print shows the modified list, only the widget that doesn’t look like rebuild after the day clicked, even though it have setstates along the way.
Can somebody help?
Code is messy cause I’m don’t know much and still haven’t clean up yet.
Page
class GalleryPage extends StatefulWidget {
const GalleryPage({super.key});
@override
State<GalleryPage> createState() => _GalleryPageState();
}
class _GalleryPageState extends State<GalleryPage> {
var logger = Logger();
final GlobalKey<CalendarState> _calendarKey = GlobalKey();
List<dynamic> studentsList = [];
Uint8List? savedPhoto;
void _deleteSelectedDayData() {
_calendarKey.currentState?.deleteSelectedDayData();
}
void sendDataToBackend(data) {
logger.i(data);
}
void updateGallery(List<dynamic> updatedStudentsList, Uint8List? updatedPhoto) {
setState(() {
studentsList = updatedStudentsList;
savedPhoto = updatedPhoto;
});
}
@override
Widget build(BuildContext context) {
var selectedDay = _calendarKey.currentState?.getSelectedDay();
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
if (studentsList.isNotEmpty) {
sendDataToBackend(studentsList);
} else {
showMyToast(context, 'Sem Dados', 'Não há dados para serem enviados', 'info');
}
},
child: const Icon(Icons.send_rounded),
),
appBar: AppBar(
title: const Text('Fotos Salvas'),
actions: [
IconButton(
onPressed: _deleteSelectedDayData,
icon: const Icon(
Icons.delete_outline_rounded,
color: Colors.red,
),
),
],
),
body: Column(
children: [
Calendar(
key: _calendarKey,
onGalleryUpdate: updateGallery,
),
const Spacer12Height(),
if (savedPhoto != null) PhotoGallery(savedPhoto: savedPhoto),
const Spacer12Height(),
GalleryStudentsList(studentsList: studentsList, selectedDay: selectedDay),
],
),
);
}
}
Calendar controller
class CalendarController {
final SharedPreferences prefs;
var logger = Logger();
CalendarController(this.prefs);
DateTime normalizeDate(DateTime date) {
return DateTime(date.year, date.month, date.day);
}
Future<Map<DateTime, List<Event>>> loadEvents() async {
String? savedDateListJson = prefs.getString('savedDateList');
Map<DateTime, List<Event>> events = {};
if (savedDateListJson != null) {
List<String> savedDates = List<String>.from(jsonDecode(savedDateListJson));
for (String date in savedDates) {
DateTime parsedDate = DateFormat('yyyy-MM-dd').parse(date);
DateTime normalizedDate = normalizeDate(parsedDate);
events.putIfAbsent(normalizedDate, () => [Event('Event on $date')]);
}
}
return events;
}
Future<List<dynamic>> fetchStudentsForDay(DateTime selectedDay) async {
String formattedDate = DateFormat('yyyy-MM-dd').format(selectedDay);
String? studentsListJson = prefs.getString('savedList_$formattedDate');
if (studentsListJson == null) return [];
try {
return List<dynamic>.from(jsonDecode(studentsListJson));
} catch (e) {
logger.e('Error parsing students list: $e');
return [];
}
}
Future<Uint8List?> fetchPhotoForDay(DateTime selectedDay) async {
String formattedDate = DateFormat('yyyy-MM-dd').format(selectedDay);
String? savedPhotoString = prefs.getString('savedPhoto_$formattedDate');
if (savedPhotoString != null) {
return base64Decode(savedPhotoString);
}
return null;
}
Future<void> deleteDayData(DateTime selectedDay) async {
DateTime normalizedSelectedDay = normalizeDate(selectedDay);
String formattedDate = DateFormat('yyyy-MM-dd').format(normalizedSelectedDay);
String? savedDateListJson = prefs.getString('savedDateList');
if (savedDateListJson != null) {
List<String> savedDates = List<String>.from(jsonDecode(savedDateListJson));
savedDates.removeWhere((date) => date == formattedDate);
await prefs.setString('savedDateList', jsonEncode(savedDates));
}
await prefs.remove('savedList_$formattedDate');
await prefs.remove('savedPhoto_$formattedDate');
}
Future<void> saveManualIdentification(List<dynamic> students, DateTime selectedDay) async {
String formattedDate = DateFormat('yyyy-MM-dd').format(selectedDay);
String updatedStudentsListJson = jsonEncode(students);
await prefs.setString('savedList_$formattedDate', updatedStudentsListJson);
}
}
Calendar Widget
class Calendar extends StatefulWidget {
final Function(List<dynamic>, Uint8List?) onGalleryUpdate;
const Calendar({super.key, required this.onGalleryUpdate});
@override
CalendarState createState() => CalendarState();
}
class CalendarState extends State<Calendar> {
late final CalendarController _calendarHelper;
late final ValueNotifier<List<Event>> _selectedEvents;
CalendarFormat _calendarFormat = CalendarFormat.week;
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
Map<DateTime, List<Event>> _events = {};
List<dynamic> allStudents = [];
List<dynamic> studentsForSelectedDay = [];
Uint8List? _savedPhoto;
bool _isLoading = true;
@override
void initState() {
super.initState();
_selectedDay = _focusedDay;
_selectedEvents = ValueNotifier([]);
_initialize();
}
@override
void dispose() {
_selectedEvents.dispose();
super.dispose();
}
Future<void> _initialize() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_calendarHelper = CalendarController(prefs);
_events = await _calendarHelper.loadEvents();
_selectedEvents.value = _getEventsForDay(_selectedDay!);
await fetchData();
setState(() {
_isLoading = false;
});
}
List<Event> _getEventsForDay(DateTime day) {
DateTime normalizedDay = _calendarHelper.normalizeDate(day);
return _events[normalizedDay] ?? [];
}
Future<void> fetchData() async {
allStudents = await _calendarHelper.fetchStudentsForDay(_selectedDay!);
_savedPhoto = await _calendarHelper.fetchPhotoForDay(_selectedDay!);
setState(() {
studentsForSelectedDay = allStudents;
});
widget.onGalleryUpdate(studentsForSelectedDay, _savedPhoto);
}
Future<void> _onDaySelected(DateTime selectedDay, DateTime focusedDay) async {
if (!isSameDay(_selectedDay, selectedDay)) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
_selectedEvents.value = _getEventsForDay(selectedDay);
fetchData();
});
}
}
void deleteSelectedDayData() async {
if (_selectedDay != null) {
await _calendarHelper.deleteDayData(_selectedDay!);
setState(() {
_events.remove(_calendarHelper.normalizeDate(_selectedDay!));
studentsForSelectedDay.clear();
});
widget.onGalleryUpdate([], null);
}
}
DateTime? getSelectedDay() {
return _selectedDay;
}
List<dynamic> getStudentsList() {
return studentsForSelectedDay;
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Column(
children: [
TableCalendar<Event>(
firstDay: kFirstDay,
lastDay: kLastDay,
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
enabledDayPredicate: (day) {
return day.isBefore(DateTime.now().add(const Duration(days: 1)));
},
calendarFormat: _calendarFormat,
eventLoader: _getEventsForDay,
startingDayOfWeek: StartingDayOfWeek.sunday,
locale: 'pt_BR',
availableCalendarFormats: const {
CalendarFormat.month: 'Mensal',
CalendarFormat.week: 'Semanal',
},
calendarStyle: const CalendarStyle(
markerDecoration: BoxDecoration(
color: Color(0xffef6c00),
shape: BoxShape.circle,
),
outsideDaysVisible: false,
),
onDaySelected: _onDaySelected,
onFormatChanged: (format) {
if (_calendarFormat != format) {
setState(() {
_calendarFormat = format;
});
}
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
),
],
);
}
}
List Widget
class GalleryStudentsList extends StatefulWidget {
final List<dynamic> studentsList;
final DateTime? selectedDay;
const GalleryStudentsList({
super.key,
required this.studentsList,
required this.selectedDay,
});
@override
State<GalleryStudentsList> createState() => _GalleryStudentsListState();
}
class _GalleryStudentsListState extends State<GalleryStudentsList> {
late final CalendarController _calendarHelper;
@override
void initState() {
super.initState();
_initialize();
}
Future<void> _initialize() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_calendarHelper = CalendarController(prefs);
}
@override
Widget build(BuildContext context) {
return widget.studentsList.isNotEmpty
? Expanded(
child: ListView.builder(
itemCount: widget.studentsList.length,
itemBuilder: (BuildContext context, int index) {
final student = widget.studentsList[index];
final String name = student['individualName'] ?? 'Não identificado';
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 80),
child: ScaleAnimation(
duration: const Duration(milliseconds: 80),
curve: Curves.fastOutSlowIn,
child: FadeInAnimation(
child: StudentCardDetection(
showCheckBox: true,
studentName: name,
studentAttendanceSituation: student['isIdentified'] == 1,
onManualIdentification: (bool? studentAttendanceSituation) async {
setState(() {
student['isIdentified'] = studentAttendanceSituation == true ? 1 : 0;
});
await _calendarHelper.saveManualIdentification(widget.studentsList, widget.selectedDay!);
await _calendarHelper.fetchStudentsForDay(widget.selectedDay!);
},
),
),
),
);
},
),
)
: const Center(
child: Text('Não existem dados para o dia selecionado', style: TextStyle(fontSize: 20), textAlign: TextAlign.center),
);
}
}
1