A Compose Multiplatform calendar for displaying and interacting with Hijri (Islamic) dates.
- Full month grid (6 weeks), month navigation, date selection
- Arabic-Indic numerals and RTL support
- Customizable colors, today indicator, weekend styling
- Configurable first day of week (default Saturday)
- Animated transitions, accessibility support
- Android (minSdk 26), iOS (arm64), Desktop (JVM)
Distributed via JitPack.
repositories { maven("https://jitpack.io") }
dependencies {
implementation("com.github.muazdev26.HijriCalendar:hijri-calendar-compose:2.3.2")
}val state = rememberHijriCalendarState(initialMonth = HijrahYearMonth(1447, 9))
HijriCalendar(state = state, onDayClick = state.defaultOnDayClick())@Composable
fun HijriCalendar(
state: HijriCalendarState,
modifier: Modifier = Modifier,
colors: HijriCalendarColors = HijriCalendarDefaults.colors(),
useArabicIndicNumerals: Boolean = false,
dateDisplayMode: DateDisplayMode = DateDisplayMode.HIJRI_ONLY,
dayCellSize: Dp? = null,
onDayClick: (CalendarDay) -> Unit,
dayContent: (@Composable (CalendarDay) -> Unit)? = null,
header: (@Composable () -> Unit)? = null,
labels: HijriCalendarLabels = HijriCalendarDefaults.labels(),
ignoreFontScale: Boolean = false,
)No year/month picker, range/multi-select, or fixed-height mode. Navigate one month at a time via arrows/swipe, or state.goToMonth(...).
class HijriCalendarState(
initialMonth: HijrahYearMonth,
initialSelectedDate: HijrahDate? = null,
firstDayOfWeek: WeekDay = WeekDay.SATURDAY,
minDate: HijrahDate? = null,
maxDate: HijrahDate? = null,
adjustmentDays: Int = 0,
weekendDays: Set<WeekDay> = setOf(WeekDay.FRIDAY, WeekDay.SATURDAY),
)Properties: currentMonth, selectedDate, calendarMonth, adjustmentDays, weekendDays, canGoToPreviousMonth, canGoToNextMonth.
Methods: goToNextMonth(), goToPreviousMonth(), selectDate(date), goToMonth(yearMonth), goToToday().
rememberHijriCalendarState / rememberSaveableHijriCalendarState, HijriCalendarColors, HijriCalendarLabels, HijriCalendarDefaults, DateDisplayMode (HIJRI_ONLY/GREGORIAN_ONLY/BOTH), defaultOnDayClick, HijriCalendarDayCell, HijriCalendarHeader. HijriCalendarGrid and HijriWeekRow are internal.
CalendarDaycarries up to three date slots (three calendars may disagree).dayOfMonthresolves them in order;localDateis the unambiguous Gregorian day.
State does not persist process death on its own. Use rememberSaveableHijriCalendarState (rotation + process death) or hoist into a ViewModel with SavedStateHandle. CalendarDay is @Serializable.
// Theming
val colors = HijriCalendarDefaults.colors(selectedDayContainerColor = Color(0xFF1976D2))
HijriCalendar(state = state, colors = colors, onDayClick = state.defaultOnDayClick())
// First day of week
rememberHijriCalendarState(initialMonth = ..., firstDayOfWeek = WeekDay.SUNDAY)
// Arabic-Indic numerals
HijriCalendar(state = state, useArabicIndicNumerals = true, onDayClick = ...)
// RTL: set layoutDirection = LayoutDirection.Rtl in CompositionLocalProvider
// Custom day content
HijriCalendar(state = state, onDayClick = ..., dayContent = { day -> Text(day.dayOfMonth.toString()) })rememberHijriCalendarState(initialMonth = ..., minDate = HijrahDate(1447, 1, 1), maxDate = HijrahDate(1447, 12, 30))Out-of-range days are disabled; navigation is bounded by the range.
dateDisplayMode: HIJRI_ONLY (default), GREGORIAN_ONLY, BOTH. Cells stay a fixed size (48.dp default via dayCellSize) regardless of mode. Text scales with the system font scale up to a cap (BothModeMaxFontScale 1.5× vs SingleLineMaxFontScale 2×); header is uncapped. Pass ignoreFontScale = true to disable scaling.
val labels = HijriCalendarLabels(
hijriMonthName = { _, month -> ... },
gregorianMonthName = { month -> ... },
weekdayShortName = { it.shortName },
previousMonthContentDescription = "...",
nextMonthContentDescription = "...",
dayContentDescription = { day -> "Day ${day.dayOfMonth}" },
)Defaults reproduce built-in English.
rememberHijriCalendarState(initialMonth = ..., weekendDays = setOf(WeekDay.FRIDAY))rememberHijriCalendarState(initialMonth = ..., adjustmentDays = 1)Shifts the whole calendar (day numbers, alignment, isToday, selection, bounds). selectedDate is already adjusted. Dates pushed out of range (~1300–1600 AH) render as disabled placeholders.
A shared projection module (calendar-widget-data, WidgetCalendar framework on iOS) computes each month grid + "today" projection, keeping both platforms in sync.
Android (Glance): five widgets ship in the library — HijriCalendarWidget (resizable month grid), HijriTodayWidget (today strip), and three 1x1 tiles HijriDateWidget/GregorianDateWidget/HijriDualDateWidget. Month grid has today's highlighted cell, weekend tint, faded out-of-month cells, on-widget month navigation, two-line Hijri/Gregorian header. Today's cell is a filled accent highlight; tapping clears it back to today. Today strip lays Gregorian + Hijri dates side by side under a weekday band. Tiles show day + month, or both dates at once (dual). Reading direction follows each widget's own language (Urdu RTL or English), not the device locale. Bundled Noto Nastaliq Urdu face is opt-in via HijriWidgetFonts(bundledUrduFont = true) — rasterised in-app, handed to the launcher as a bitmap. Settings (adjustmentDays, numeral style, first day of week, language, month names, source, pinned month for grid, bundled font, manual refresh) apply immediately with no Save step and refresh the whole family. Live previews via HijriWidgetLivePreview etc. Midnight + TIME_CHANGED/TIMEZONE_CHANGED/LOCALE_CHANGED/BOOT_COMPLETED refresh with a WorkManager 24h backstop; background renders skipped while foregrounded. Deep-link on tap to hijricalendar://today. Android 15+ uses providePreview; 12–14 use static previewLayout XMLs.
iOS (WidgetKit, 17+): systemSmall/Medium/Large, one timeline entry per day, deep-links to hijricalendar://today.
dependencies { implementation("com.github.muazdev26.HijriCalendar:hijri-calendar-widget-glance:2.3.2") }The library's manifest declares all five receivers; widgets work on defaults. Optional setup:
- In
Application.onCreate:HijriWidgetRefreshScheduler.schedule(this)+ registerHijriWidgetForegroundWatcher. - Handle the
hijricalendar://todaydeep link on your root activity. - (Optional) Build a settings screen using
HijriWidgetConfig,widgetOptionsSaver(),HijriWidgetRefresher, and the live-preview composables. Override the widget-info XML in your app to addandroid:configure.
Uses HijrahDateTime and kotlinx-datetime.
MIT © 2026 Muhammad Muaz