syncfusion / flutter-widgets

Syncfusion Flutter widgets libraries include high quality UI widgets and file-format packages to help you create rich, high-quality applications for iOS, Android, and web from a single code base.
1.56k stars 758 forks source link

[syncfusion_flutter_calendar] appointments are shown only after screen touch (iOS) #1869

Open rmpt opened 4 months ago

rmpt commented 4 months ago

Bug description

On any view type (month, day, week, etc), appoitments are added to the data source correctly, the notify functions is called but appointments are not shown. Once I touch the screen and do a minimal movement/swipe, all appointments show up. It means they are there, but now being rendered.

Steps to reproduce

  1. Open - for instance - month view
  2. when onViewChanged gets called, load some appointments asynchronously
  3. noting is shown on the month view
  4. slide the view 1px and everything will be shown

Code sample

Widget code (simplified) ```dart class _MonthViewWidgetState extends State

Screenshots or Video

https://github.com/syncfusion/flutter-widgets/assets/16465379/701e8469-b676-4746-aede-19d0c64ab633

Stack Traces

n/a

On which target platforms have you observed this bug?

iOS

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.22.0, on macOS 14.0 23A344 darwin-arm64, locale pt-PT) [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 15.4) [✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome) ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable. [!] Android Studio (not installed) [✓] IntelliJ IDEA Community Edition (version 2024.1.1) [✓] VS Code (version 1.89.1) [✓] Connected device (3 available) [✓] Network resources ```
rmpt commented 4 months ago

video added

PreethikaSelvam commented 4 months ago

Hi @rmpt,

We have analyzed your query and if you're trying to load an appointment when the view changes, we suggest you use the handleLoadMore, which allows you to add the appointments to the data source and notify the listener to update the appointment in the current view. We have shared a UG documentation, KB, and live sample below for your reference.

UG: https://help.syncfusion.com/flutter/calendar/load-more#load-appointments

KB Link: https://support.syncfusion.com/kb/article/11218/how-to-load-appointments-on-demand-in-flutter-calendar

Live sample: https://github.com/syncfusion/flutter-examples/blob/master/lib/samples/calendar/calendar_loadmore.dart

If you are still facing any issue, please provide the reproduced sample this will be more helpful to us to provide a solution sooner.

Regards,

Preethika Selvam.

rmpt commented 4 months ago

Ok I managed to change the logic so the appointments are loaded on the handleLoadMore callback and not on the onViewChanged, but the result is the same, please have a look at the video.

https://github.com/syncfusion/flutter-widgets/assets/16465379/edb4fffa-27a3-4467-bdfb-7ea490774a8e

I do the API call from my data source within handleLoadMore call. Data is corretcly loaded, added to appointments and notified, but the events are only shown after a minimum swipe.

Simplified code:

class AgendaDataSource extends CalendarDataSource {

  ...

  @override
  Future<void> handleLoadMore(DateTime start, DateTime end) async {
    // async API call 
    ApiCall.getEvents(start, end).then((entries) {
        appointments!.addAll(entries);
        notifyListeners(CalendarDataSourceAction.add, entries);
    });
  }
}

Does it has to do with the API call? In your examples you data is local, so all operations within handleLoadMore are sync. In my case, I'm calling the API to fetch some data and only after I have the data, I add and notify the data source. Does it make any difference?

rmpt commented 3 months ago

Hi @PreethikaSelvam , I found the issue. The problem was, before adding the entries, I was removing others that were not needed anymore and notifying to remove. It means the flow was something like:

class AgendaDataSource extends CalendarDataSource {

  ...

  @override
  Future<void> handleLoadMore(DateTime start, DateTime end) async {

    // delete unnecessary entries
    var entriesToDelete = appointments!.where(predicate).toList();
    for (var entry in entriesToDelete) {
      appointments!.remove(event);
    }
    notifyListeners(CalendarDataSourceAction.remove, entriesToDelete);

    // async API call 
    ApiCall.getEvents(start, end).then((entries) {
        appointments!.addAll(entries);
        notifyListeners(CalendarDataSourceAction.add, entries);
    });
  }
}

I had 2 sequential notifyListeners, so I guess the first one would trigger the rendering and the second one would be "ignored" until the screen is touched.

But now I have another question: I still need to delete and add entries, what CalendarDataSourceAction should I use? reset?

marcoancona commented 1 month ago

We see the same issue: the calendar view is only updated after a user input (like a tap anywhere). This feels very much like a race condition because it does not happen all the times.

@PreethikaSelvam You suggestion to use handleLoadMore does not work in all cases. What if events are updated in the background and we need to trigger a calendar update to show the new data (so not triggered by a change of view or a change of date)?

EIDT: It turns out that this was probably a race condition caused by calling calendarController.displayDate = twice in a row in a short time span. After fixing this, the problem no longer occurs.