kizitonwose / Calendar

A highly customizable calendar view and compose library for Android.
MIT License
4.5k stars 492 forks source link

Scroll is slightly laggy in Compose Vertical calendar #497

Closed shriharsha-bhagwat closed 6 months ago

shriharsha-bhagwat commented 9 months ago

Library information:

Describe the bug**

Scroll is slightly laggy in Vertical calendar of Compose module. Can be seen in Example 2 as well. Its not very smooth like View module. The same behaviour is seen also when animateScroll method is called with particular month.

To Reproduce (if applicable)

Steps to reproduce the behavior:

  1. Go to Example 2
  2. Scroll up and down
  3. See that scroll lags a bit

Expected behavior (if applicable)

Smooth scroll in compose module as well is needed.

kizitonwose commented 7 months ago

Hi, I had some time to look into this today and I do not notice any obvious lags in the sample project. Here's a recording of Example 2, when the page opens initially, we scroll to another month using animateScrollToMonth after which we do some random manual scrolling.

However, I know that the sample project may not reflect a real-world scenario as the latter likely has a lot more going on. I will advise you to look into the lambda functions passed into the calendar composables dayContent, monthFooter, monthHeader, monthBody, monthContainer and see if they are causing unnecessary recomposition.

The likely culprit is the dayContent which will be called approximately 30 times for each month, it would be safer to extract this into a remember block.

The current implementation in the sample looks like this:

val state = rememberCalendarState(
    startMonth = startMonth,
    endMonth = endMonth,
    firstVisibleMonth = currentMonth,
    firstDayOfWeek = daysOfWeek.first(),
)
VerticalCalendar(
    state = state,
    dayContent = { value ->
        Day(
            day = value,
            today = today,
            selection = selection,
        ) { day ->
            if (day.position == DayPosition.MonthDate &&
                (day.date == today || day.date.isAfter(today))
            ) {
                selection = getSelection(
                    clickedDate = day.date,
                    dateSelection = selection,
                )
            }
        }
    },
    monthHeader = { month -> MonthHeader(month) },
)

I'll advise you to change it to something like this:

val state = rememberCalendarState(
    startMonth = startMonth,
    endMonth = endMonth,
    firstVisibleMonth = currentMonth,
    firstDayOfWeek = daysOfWeek.first(),
)
val onClick = remember {
    { day: CalendarDay ->
        if (day.position == DayPosition.MonthDate &&
            (day.date == today || day.date.isAfter(today))
        ) {
            selection = getSelection(
                clickedDate = day.date,
                dateSelection = selection,
            )
        }
    }
}
VerticalCalendar(
    state = state,
    dayContent = { value ->
        Day(
            day = value,
            today = today,
            selection = selection,
            onClick = onClick,
        )
    },
    monthHeader = { month -> MonthHeader(month) },
)
kizitonwose commented 6 months ago

Closing this as it is likely not an issue with the library itself.