I'm using Riverpod and Syncfusion Calendar. To display event on the calendar. As the user swipes from month to month, I need the app to refesh the api call to get the events for the new month.
Now the method _onViewChanged is called when the date changes on the view, and is also called the first time the view loads. See here.
Now in order to trigger the api call again I call, ref.invalidate(myProvider) inside this method. But this is causing the following error:
Unhandled Exception: Tried to modify a provider while the widget tree was building. If you are encountering this error, chances are you tried to modify a provider in a widget life-cycle, such as but not limited to:
- build
- initState
- dispose
- didUpdateWidget
- didChangeDepedencies
Modifying a provider inside those life-cycles is not allowed, as it could lead to an inconsistent UI state. For example, two widgets could listen to the same provider, but incorrectly receive different states.
Here is my class:
class CalendarScreen extends ConsumerStatefulWidget {
const CalendarScreen({
Key? key,
}) : super(key: key);
@override
ConsumerState createState() => _CalendarScreenState();
}
final List<CalendarView> _allowedViews = <CalendarView>[
CalendarView.day,
CalendarView.week,
CalendarView.workWeek,
CalendarView.month,
CalendarView.schedule
];
class _CalendarScreenState extends ConsumerState<CalendarScreen> {
late AsyncValue<List<CalendarResponse>> calendarResponse;
CalendarController _calendarController = CalendarController();
@override
Widget build(BuildContext context) {
calendarResponse = ref.watch(calendarResponseProvider);
print('2124: Calendar');
return Scaffold(
appBar: AppBar(title: const Text('Calendar'),),
body: calendarResponse.when(data: (data) {
EasyLoading.dismiss(animation: false);
return SfCalendar(
controller: _calendarController,
view: CalendarView.month,
showDatePickerButton: true,
allowedViews: _allowedViews,
dataSource: MyCalendarDataSource(getAppointments(data)),
onViewChanged: _onViewChanged,
monthViewSettings: const MonthViewSettings(
appointmentDisplayMode: MonthAppointmentDisplayMode.appointment,
showTrailingAndLeadingDates: true,
appointmentDisplayCount: 3,
agendaItemHeight: 50,
showAgenda: true,
),
appointmentTextStyle: const TextStyle(
fontSize: 7,
),
);
}, error: ((error, stackTrace) {
EasyLoading.dismiss();
return Text('Error: ${error.toString()}');
}), loading: (() {
EasyLoading.show();
return const Center();
// return const Center(
// child: CircularProgressIndicator(),
// );
}),
skipLoadingOnRefresh: false,
)
);
}
void _onViewChanged(ViewChangedDetails visibleDatesChangedDetails) async {
DateTime sDate = visibleDatesChangedDetails.visibleDates.first;
DateTime eDate = visibleDatesChangedDetails.visibleDates.last;
print('Start month: $sDate. End month: $eDate');
print('Display date: ${_calendarController.displayDate}');
ref.read(startDateCalendarScreenProvider.notifier).state = sDate;
ref.read(endDateCalendarScreenProvider.notifier).state = eDate;
ref.invalidate(calendarResponseProvider);
}
}
How can I call the invalidate method only when the dates are changed?
Regarding _onViewChanged being called when the date changes on the view, and also being called the first time the view loads:
Based on the information you provided, we have reviewed your query. When the calendar loads the visible dates initially, the onViewChanged event will be called. The onViewChanged event will also be called in the following scenarios:
• When the calendar view is swiped to the previous/next view.
• When the calendar view is changed, such as from month to day, etc.
• When navigated to a specific date programmatically using the controller.displayDate.
• When navigated programmatically using controller.forward and controller.backward.
If you want to restrict the first-time view load, you can handle your requirement inside the event based on the current view date.
For more details about the onViewChanged callback from the calendar, please refer to our UG documentation:
https://help.syncfusion.com/flutter/calendar/callbacks#view-changed-callback.
Regarding calling ref.invalidate(myProvider) inside the viewchanged method and causing an error:
The viewChanged event will be triggered before the Calendar widget build cycle is completed. If you perform any operations such as calling Set State, etc. inside the view changed event, it will cause the calendar widget build cycle to trigger again. We suspect that the reported crash occurred like these scenarios. Therefore, we suggest that you handle the code inside the addPostFrameCallback method. For more details about addPostFrameCallback, kindly refer to the following link
https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html.