Hello to all.
I have a button that is in charge of performing the data search. I want that while the data reading operation is executing, a CircularProgressIndicator appears instead of the calendar. For this I use a Visibility object.
The problem arises when the reading operation finishes and the calendar is displayed again; it does not reflect the situation of its DataSource until I move the mouse. It is very strange.
I attach an example I have made to demonstrate the problem.
Best regards
Hi Fernando,
Thank you for letting us know about this issue. We can replicate the reported issue only in the web platform when using the CircularProgressIndicator and a Future.delayed. Currently, we are investigating the issue and will provide further details within two business days, by November 19, 2024. We appreciate your patience until then.
Regards,
Preethika Selvam.
Hi Fernando,
We have analyzed your query, and the problem arises because when the Visibility widget switches from showing the Circular Progress Indicator to the calendar, it may not immediately trigger a refresh in the SfCalendar widget to reflect the latest data from the dataSource.
The issue here is that the Visibility widget simply hides or shows the widgets but does not force a rebuild of the calendar. Therefore, even after the data reading operation finishes, the SfCalendar might not update immediately, and you have to interact with the UI (like moving the mouse) to force a refresh.
To resolve this issue, you can use the loadMoreWidgetBuilder property of the SfCalendar. This ensures that when the reading operation finishes and the calendar is displayed again, it will automatically reflect the situation of its dataSource without requiring any manual interaction.
Here’s the code snippet based on the suggestion:
loadMoreWidgetBuilder: (context, loadMoreAppointments) {
return FutureBuilder(
future: loadMoreAppointments(),
builder: (context, snapshot) {
if (!searching) {
return const SizedBox(); // No spinner once the data is loaded
}
return Container(
height: _calendarController.view == CalendarView.schedule ? 50 : double.infinity,
width: double.infinity,
alignment: Alignment.center,
child: const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.blue),
),
);
},
);
},
In this approach, loadMoreWidgetBuilder listens for updates to the loadMoreAppointments and ensures that while the data is being fetched, the calendar displays the loading spinner correctly. Once the data is ready, the progress indicator will be hidden and the calendar will update immediately by reflecting the latest data from the data source.
You can modify this solution based on your requirements.
Regards,
Kaviyarasan Arumugam
Hi Kaviyarasan.
The solution fixes the problem but now I cannot change the month as the controls to do so are disabled. Any idea?
Thank you.
Hi Fernando,
We would like to let you know that, while using the loadMoreWidgetBuilder in the SfCalendar for on-demand loading, we need to override the handleLoadMore method to handle the logic that needs to be done while loading.
If we used both the loadMoreWidgetBuilder and handleLoadMore methods in the SfCalendar, we can able to display the CircularProgressIndicator while navigating to the next or previous month itself as similar to the knowledge base article which has been shared below. But in this case, there is no need to display the search icon as we load the data while navigating itself.
How to load appointments On-Demand in Flutter Calendar?
However, we have also achieved the same expected requirement by simply removing the Visibility widget. Here, we have displayed the CircularProgressIndicator above the SfCalendar Widget using the Stack Widget based on some condition while tapping the search icon button as shown in the code snippet below.
Expanded( child: Stack( children: [ SfCalendar( view: CalendarView.month, dataSource: DataSource(appointments), ), if (searching) const Center( child: CircularProgressIndicator(), ), ], ), ); |
Additionally, if you want to hide the SfCalendar while displaying the CircularProgressIndicator, you can restrict it by simply by checking the condition that the boolean property searching is false. So that, the SfCalendar will be displayed only after loading.
Also attached the sample and demo below for your reference.
If you have further queries, please free to reach us!.
Regards,
Kaviyarasan Arumugam.
Hi.
If you want to hide the SfCalendar, the Stack is not a solution either because the same thing happens as with the Visibility, i.e., the SfCalendar is not being repainted until an external event (mouse movement, browser resizing...).
Hi Fernando de Miguel,
We have replicated the reported issue regarding 'The calendar is not visually updated when wrapping with the visibility widget in web platform' on our end and have logged a bug report for it in our feedback portal. The issue is scheduled to be fixed in the first week following the main release, which is expected in the second week of December 2024. We will notify you here once the release has been rolled out, and we appreciate your patience in the meantime. You can also track the status of the bug using the feedback link provided below.
Regards,
Naveen. K
Hi Fernando,
Sorry for the delay, we have analyzed the issue and found that the appointments were not being triggered immediately when the calendar was rendered. To resolve the issue, we have added a Builder inside the Visibility widget. In the builder, we used addPostFrameCallback to call setState after the initial frame was drawn. This ensures that the calendar and appointments are correctly rendered immediately after the loading indicator disappears, without requiring any user interaction. The appointments are now displayed as expected, ensuring a smooth user experience from the start. We have shared a modified code snippet, and a sample for your reference.
Code Snippet:
|
@override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Column( children: [ SizedBox( height: 100.0, child: IconButton( onPressed: () async { setState(() { searching = true; }); await _readAppointments(); setState(() { searching = false; }); }, icon: const Icon(Icons.search)), ), Expanded( child: Visibility( visible: !searching, replacement: const Center( child: CircularProgressIndicator(), ), child: Builder(builder: (context) { WidgetsBinding.instance.addPostFrameCallback((_) { if (!searching) { setState(() {}); } }); return SfCalendar( view: CalendarView.month, dataSource: DataSource(appointments), monthViewSettings: const MonthViewSettings( showAgenda: true, ), monthCellBuilder: (context, details) => getMonthBuilder(details), ); }), )), ], ), ), ); } |
Output:
Please let us know if you need any further
assistance.
Regards,
Preethika Selvam.
Hi Preethika and happy new year :)
I do not believe that the proposed solution is entirely correct. In the end, what has been proposed as a solution causes the calendar to be continually being built; and this causes problems. One example I have encountered is the interaction with the appointments at the bottom. I have created my own appointments builder using the appointmentBuilder property. It was a GestureDetector that in the onDoubleTap event I wanted to show a dialog; and with the proposed solution, this one is not shown. I suppose that it has to do with the continuous construction of the calendar object.
Anyway, in my real application, taking up this issue today after a few weeks, I do not understand why, but the problem described in this thread does not occur. So for my part, we can close this issue.
Anyway, thank you very much for your time.
Best regards
Hi Fernando,
Happy New Year!
Thank you for your feedback and for letting us know that the issue is no longer occurring. We truly appreciate your insights. Please feel free to reach out if you have any further queries, we are always happy to assist you.
Best regards,
Preethika Selvam.