iTranslated by AI
[Android] Trying out the Calendar library
Calendar
A highly customizable calendar library for Android, utilizing RecyclerView for the View system and LazyRow/LazyColumn for Compose.
This is a note from when I briefly tried out the Calendar library, which is useful for handling calendars within Android apps.
Verification Environment
$ sw_vers
ProductName: macOS
ProductVersion: 13.4
BuildVersion: 22F66
Android Studio Giraffe | 2022.3.1 Patch 1
Features of the Calendar Library
- Single, multiple, and range selection - perform date selection using your preferred method
- Week or month mode - display week-based calendars or standard monthly calendars
- Disable specific dates to make them unselectable
- Restrict the calendar's date range
- Custom date view/composable
- Custom calendar view/composable
- Use any day as the first day of the week
- Horizontal or vertical scrolling calendars
Package Installation
As described here, you specify the Calendar version according to the Compose UI version.
Since I'm using compose bom version 2022.10.00 this time, referring here shows that the Compose UI version is 1.3.0, so I'll use 2.2.0.
Add the following to libs.versions.toml:
[versions]
calendar-view = "2.2.0"
[libraries]
calendar-view = { module = "com.kizitonwose.calendar:compose", version.ref = "calendar-view" }
Add the following to app/build.gradle.kts:
dependencies {
implementation(libs.calendar.view)
}
Documentation
Calendar/docs/Compose.md at main · kizitonwose/Calendar
I will proceed by referring to the link above.
Four Main Composables
- HorizontalCalendar
- Monthly calendar with horizontal scrolling
- VerticalCalendar
- Monthly calendar with vertical scrolling
- WeekCalendar
- Weekly calendar with horizontal scrolling
- HeatMapCalendar
- Like the contribution graph on GitHub
First, let's try a minimal implementation with HorizontalCalendar
@Composable
fun CalendarCompose() {
val currentMonth = remember { YearMonth.now() }
val startMonth = remember { currentMonth.minusMonths(100) } // Adjust as needed
val endMonth = remember { currentMonth.plusMonths(100) } // Adjust as needed
val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library
val state = rememberCalendarState(
startMonth = startMonth,
endMonth = endMonth,
firstVisibleMonth = currentMonth,
firstDayOfWeek = firstDayOfWeek
)
Column(modifier = Modifier.fillMaxSize()) {
HorizontalCalendar(
state = state,
dayContent = { Day(it) }
)
}
}
@Composable
private fun Day(day: CalendarDay) {
Box(
modifier = Modifier
.aspectRatio(1f), // This is important for square sizing!
contentAlignment = Alignment.Center
) {
Text(text = day.date.dayOfMonth.toString())
}
}
The following shows how it looks when running on an emulator.

Behavior of VerticalCalendar, WeekCalendar, and HeatMapCalendar
Next, I'll try using each type of calendar.
-
VerticalCalendar
Just changing HorizontalCalendar to VerticalCalendar looks like this:

-
WeekCalendar
@Composable fun WeekCalendarSimple() { val currentDate = remember { LocalDate.now() } val currentMonth = remember { YearMonth.now() } val startDate = remember { currentMonth.atStartOfMonth() } // Adjust as needed val endDate = remember { currentMonth.atEndOfMonth() } // Adjust as needed val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library val state = rememberWeekCalendarState( startDate = startDate, endDate = endDate, firstVisibleWeekDate = currentDate, firstDayOfWeek = firstDayOfWeek ) Column(modifier = Modifier.fillMaxSize()) { WeekCalendar( state = state, dayContent = { Day(it) } ) } } @Composable private fun Day(day: WeekDay) { Box( modifier = Modifier .aspectRatio(1f), // This is important for square sizing! contentAlignment = Alignment.Center ) { Text(text = day.date.dayOfMonth.toString()) } }
-
HeatMapCalendar
@Composable fun HeatMapCalendarSimple() { val currentMonth = remember { YearMonth.now() } val endDate = remember { LocalDate.now() } val startDate = remember { endDate.minusMonths(12) } val state = rememberHeatMapCalendarState( startMonth = startDate.yearMonth, endMonth = endDate.yearMonth, firstVisibleMonth = endDate.yearMonth, firstDayOfWeek = firstDayOfWeekFromLocale(), ) Column(modifier = Modifier.fillMaxSize()) { HeatMapCalendar( state = state, dayContent = { day, week -> Day(day, startDate, endDate, week) } ) } } @Composable private fun Day( day: CalendarDay, startDate: LocalDate, endDate: LocalDate, week: HeatMapWeek, ) { val weekDates = week.days.map { it.date } if (day.date in startDate..endDate) { LevelBox(Color.Green) } else if (weekDates.contains(startDate)) { LevelBox(Color.Transparent) } } @Composable private fun LevelBox(color: Color) { Box( modifier = Modifier .size(18.dp) // Must set a size on the day. .padding(2.dp) .clip(RoundedCornerShape(2.dp)) .background(color = color) ) }
Adding a WeekTitle
The documentation can be found here.
@Composable
fun HorizontalCalendarWeekTitle() {
val currentMonth = remember { YearMonth.now() }
val startMonth = remember { currentMonth.minusMonths(100) } // Adjust as needed
val endMonth = remember { currentMonth.plusMonths(100) } // Adjust as needed
val daysOfWeek = remember { daysOfWeek() }
val state = rememberCalendarState(
startMonth = startMonth,
endMonth = endMonth,
firstVisibleMonth = currentMonth,
firstDayOfWeek = daysOfWeek.first()
)
Column(modifier = Modifier.fillMaxSize()) {
DaysOfWeekTitle(daysOfWeek = daysOfWeek)
HorizontalCalendar(
state = state,
dayContent = { Day(it) }
)
}
}
@Composable
fun DaysOfWeekTitle(daysOfWeek: List<DayOfWeek>) {
Row(modifier = Modifier.fillMaxWidth()) {
for (dayOfWeek in daysOfWeek) {
Text(
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center,
text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()),
)
}
}
}
@Composable
private fun Day(day: CalendarDay) {
Box(
modifier = Modifier
.aspectRatio(1f), // This is important for square sizing!
contentAlignment = Alignment.Center
) {
Text(text = day.date.dayOfMonth.toString())
}
}
The following shows how it looks when running on an emulator.

Thoughts
Although I've only tried it briefly, I feel that this is a highly customizable library, just as the documentation says, based on a simple base calendar.
I'll write another article when I dive deeper into it.
Discussion