Kalendar

KalendarYear#

A year overview: one page per year, rendered as a vertically scrollable column of all twelve month grids. The header's arrow buttons (labelled with the year) and a horizontal swipe drive the same KalendarViewState.

Use it as a "pick a month" or "year at a glance" screen.

The top of the 2026 year page: the year in the header, then the January and February month grids stacked in one scrolling column. The 15th of January is selected and filled with a circle, several days carry event dots, and a three-day event runs across the 9th to the 11th of February.The top of the 2026 year page: the year in the header, then the January and February month grids stacked in one scrolling column. The 15th of January is selected and filled with a circle, several days carry event dots, and a three-day event runs across the 9th to the 11th of February.

The picture shows the first two of the twelve grids — the column scrolls through the rest.

Minimal example#

kotlin
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.himanshoe.kalendar.KalendarYear
import com.himanshoe.kalendar.state.rememberKalendarYearState
import kotlinx.datetime.TimeZone
import kotlinx.datetime.todayIn
import kotlin.time.Clock

@Composable
fun YearOverview() {
    val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
    val state = rememberKalendarYearState(initialDate = today)
    var selected by remember { mutableStateOf(setOf(today)) }

    KalendarYear(
        selectedDate = today,
        state = state,
        selectedDates = selected,
        onDateClick = { date, _ -> selected = setOf(date) },
    )
}

Parameters#

ParameterTypeDefaultDescription
selectedDateLocalDateDefault single-selection highlight, and the initially visible year when state is left at its default. Superseded by selectedDates when that is passed.
modifierModifierModifierApplied to the outermost container.
stateKalendarViewStaterememberKalendarYearState(selectedDate)Navigation state. Hoist it to read visibleDate or navigate from your own UI.
eventsList<E>emptyList()Shown as indicator dots on day cells, and as span bars for multi-day events, across all twelve grids.
selectedDatesSet<LocalDate>setOf(selectedDate)The highlighted dates. Hoist your own set for multi-select or range.
onDateClick(date: LocalDate, events: List<E>) -> Unitno-opCalled when a non-disabled date is tapped, with that date's events.
onDayEventClick((event: E) -> Unit)?nullCalled when an individual event is tapped inside a day cell's +N overflow popover.
configKalendarViewConfigKalendarViewConfig()Shared visual and behavioural settings. See Configuration.
header@Composable (KalendarHeaderScope) -> UnitKalendarHeaderDefaults.HeaderReplaces the navigation header above the year. See Customization.
dayOfWeekLabel@Composable (DayOfWeek) -> UnitKalendarHeaderDefaults.DayOfWeekLabelReplaces the content of each weekday column header, on every month grid.
dayContent(@Composable (scope: KalendarDayScope<E>) -> Unit)?nullReplaces the built-in day cell entirely, on every month grid. See Customization.

Note: no drag gestures here. KalendarYear has no onDateRangeSelect, no onEventDrop, and no sliding selection indicator — twelve grids are on screen at once, so there is no single indicator to slide and no unambiguous drag surface. Multi-select and range selection still work through selectedDates and rememberKalendarSelectionState.

State factory#

rememberKalendarYearState.

kotlin
val state = rememberKalendarYearState(
    initialDate = today,
    startDayOfWeek = DayOfWeek.SUNDAY,
    minDate = LocalDate(2020, 1, 1),
    maxDate = LocalDate(2030, 12, 31),
)
ParameterTypeDefaultDescription
initialDateLocalDatetodayThe year initially visible.
startDayOfWeekDayOfWeekMONDAYThe first column of every month grid.
minDateLocalDate?nullTurns canScrollBackward false once the previous year would start before this date's year.
maxDateLocalDate?nullTurns canScrollForward false once the next year would start after this date's year.

Layout#

Each month is a section: its name — from KalendarViewConfig.monthNameFormatter, styled with KalendarTypography.monthLabel — above a static, non-scrolling grid. Sections are separated by KalendarDimensions.monthSpacing (16.dp) and the names are inset by KalendarDimensions.sectionPadding.

The whole column is one verticalScroll, and the grids inside it are eager rather than lazy. That is deliberate: a lazy grid nested inside a scrollable parent crashes on Android and iOS.

Twelve grids is a lot of vertical space. KalendarDimensions lets you compress them:

kotlin
KalendarTheme(
    dimensions = KalendarTheme.dimensions.copy(
        dayCellPadding = 0.dp,
        monthSpacing = 8.dp,
    ),
) {
    KalendarYear(selectedDate = today)
}