Getting started#
This page goes from nothing to a calendar with working selection and events. Every snippet is real code against the current API.
1. Add the dependency#
dependencies {
implementation("com.himanshoe:kalendar:2.0.0-RC3")
}kalendar re-exports kalendar-foundation (the event model) as an api dependency, so this single
line is enough. See Install for the version-catalog and multiplatform forms.
2. Your first calendar#
Every view takes a selectedDate and nothing else is required:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.himanshoe.kalendar.KalendarMonth
import kotlinx.datetime.TimeZone
import kotlinx.datetime.todayIn
import kotlin.time.Clock
@Composable
fun FirstCalendar() {
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
KalendarMonth(selectedDate = today)
}That already gives you a full month grid with a navigation header, arrow buttons, a today button, a
month/year jump picker, swipe paging, and a highlighted today.
Note:
selectedDatedoes two jobs. It is both the default single-date highlight and, when you leavestateat its default, the page the calendar opens on. Pass an explicitstate(step 5) or an explicitselectedDates(step 3) to separate the two.
3. Add selection#
selectedDates is a plain hoisted Set<LocalDate> — the calendar never owns it, so you decide what
a tap does:
@Composable
fun SelectableCalendar() {
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
var selected by remember { mutableStateOf(setOf(today)) }
KalendarMonth(
selectedDate = today,
selectedDates = selected,
onDateClick = { date, eventsOnDate ->
selected = setOf(date)
println("Tapped $date, which has ${eventsOnDate.size} events")
},
)
}For the three common behaviours — single, multiple, range — rememberKalendarSelectionState
implements the tap logic for you and survives configuration changes and process death:
import com.himanshoe.kalendar.foundation.selection.KalendarSelectionMode
import com.himanshoe.kalendar.state.rememberKalendarSelectionState
@Composable
fun RangeCalendar() {
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
val selection = rememberKalendarSelectionState(mode = KalendarSelectionMode.Range)
KalendarMonth(
selectedDate = today,
selectedDates = selection.selectedDates,
onDateClick = { date, _ -> selection.onDateClick(date) },
)
}| Mode | Tap behaviour |
|---|---|
KalendarSelectionMode.Single (default) | Replaces the selection with the tapped date. |
KalendarSelectionMode.Multiple | Toggles the tapped date in or out of the selection. |
KalendarSelectionMode.Range | First tap starts a range, second completes it (inclusive, in either tap order). The next tap starts a fresh range. |
rememberKalendarSelectionState also takes an initialSelection: Set<LocalDate>, defaulting to
empty. selection.clear() resets everything, including a half-finished range. Changing mode
resets the state.
For anything the three modes do not cover — a minimum range length, disallowed end dates — manage
selectedDates yourself; it is just a Set.
Tip: hoist the set, don't rebuild it. Pass a
remembered or state-backedSet<LocalDate>. Building a fresh set inline on every recomposition defeats the views'rememberkeys and makes the grid recompose more than it needs to.
4. Add events#
Events implement KalendarEvent. BasicKalendarEvent is the ready-made implementation:
import androidx.compose.ui.graphics.Color
import com.himanshoe.kalendar.foundation.event.BasicKalendarEvent
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.LocalTime
val events = listOf(
BasicKalendarEvent(
date = LocalDate(2026, 8, 12),
eventName = "Team standup",
startTime = LocalDateTime(LocalDate(2026, 8, 12), LocalTime(9, 0)),
endTime = LocalDateTime(LocalDate(2026, 8, 12), LocalTime(9, 30)),
eventColor = Color(0xFF4CAF50),
),
BasicKalendarEvent(
date = LocalDate(2026, 8, 14),
endDate = LocalDate(2026, 8, 16),
eventName = "Conference",
),
)
KalendarMonth(selectedDate = today, events = events)On a grid view each single-day event becomes an indicator dot under the day number, tinted by
eventColor. The Conference event has an endDate, so it draws instead as one continuous bar
running across the 14th, 15th, and 16th — breaking only where a week does. On the
Schedule views the same list renders as duration-sized blocks instead.
Full details on the event model, multi-day spans, the +N overflow, and drag-to-reschedule are on
the Events page.
5. Own the navigation state#
Every view has a matching remember*State factory. Hoist it when you want to read the visible date
or navigate from your own UI:
import androidx.compose.runtime.rememberCoroutineScope
import com.himanshoe.kalendar.state.rememberKalendarMonthState
import kotlinx.coroutines.launch
import kotlinx.datetime.DayOfWeek
@Composable
fun StatefulCalendar() {
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
val state = rememberKalendarMonthState(
initialDate = today,
startDayOfWeek = DayOfWeek.SUNDAY,
minDate = LocalDate(2020, 1, 1),
maxDate = LocalDate(2030, 12, 31),
)
val scope = rememberCoroutineScope()
Text(text = "${state.visibleDate.month} ${state.visibleDate.year}")
KalendarMonth(selectedDate = today, state = state)
Button(onClick = { scope.launch { state.animateScrollTo(LocalDate(2026, 12, 25)) } }) {
Text("Jump to December")
}
}state.canScrollBackward and state.canScrollForward are public too, so your own chrome can
disable its buttons at the same bounds the built-in header does.
6. Style it#
Colours, type, shapes, sizing, motion, and strings all resolve through an ambient token set. In a Material 3 app the defaults already follow your colour scheme, so you may not need this at all:
import com.himanshoe.kalendar.theme.KalendarTheme
import com.himanshoe.kalendar.theme.KalendarThemeDefaults
KalendarTheme(
colors = KalendarThemeDefaults.colors().copy(selectionBackground = BrandPurple),
) {
KalendarMonth(selectedDate = today)
}If your app does not use Material 3, pass KalendarThemeDefaults.systemColors() instead — see
Theming.
Next steps#
- Views — pick the right composable and see its full parameter table.
- The headless engine — build a calendar we did not design, on the same arithmetic.
- API reference — every public symbol in both modules, by package.
- Theming — every token and its default.
- Configuration — the
KalendarViewConfigknobs shared by all nine views. - Customization — replace the day cell, the header, or an event block.
- Device sync — read and write the user's real calendar.