summaryrefslogtreecommitdiffstats
path: root/packages/flutter_calendar_carousel/lib
diff options
context:
space:
mode:
Diffstat (limited to 'packages/flutter_calendar_carousel/lib')
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/event.dart79
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/event_list.dart40
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/marked_date.dart46
-rw-r--r--packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart86
-rw-r--r--packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart1214
-rw-r--r--packages/flutter_calendar_carousel/lib/src/calendar_header.dart73
-rw-r--r--packages/flutter_calendar_carousel/lib/src/default_styles.dart48
-rw-r--r--packages/flutter_calendar_carousel/lib/src/weekday_row.dart131
8 files changed, 1717 insertions, 0 deletions
diff --git a/packages/flutter_calendar_carousel/lib/classes/event.dart b/packages/flutter_calendar_carousel/lib/classes/event.dart
new file mode 100644
index 0000000..c852a94
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/event.dart
@@ -0,0 +1,79 @@
+import 'package:flutter/material.dart';
+
+class Event implements EventInterface {
+ final DateTime date;
+ final String? title;
+ final String? description;
+ final String? location;
+ final Widget? icon;
+ final Widget? dot;
+ final int? id;
+ Event({
+ this.id,
+ required this.date,
+ this.title,
+ this.description,
+ this.location,
+ this.icon,
+ this.dot,
+ });
+
+ @override
+ bool operator ==(dynamic other) {
+ return date == other.date &&
+ title == other.title &&
+ description == other.description &&
+ location == other.location &&
+ icon == other.icon &&
+ dot == other.dot &&
+ id == other.id;
+ }
+
+ @override
+ int get hashCode => Object.hash(date, description, location, title, icon, id);
+
+ @override
+ DateTime getDate() {
+ return date;
+ }
+
+ @override
+ int? getId() {
+ return id;
+ }
+
+ @override
+ Widget? getDot() {
+ return dot;
+ }
+
+ @override
+ Widget? getIcon() {
+ return icon;
+ }
+
+ @override
+ String? getTitle() {
+ return title;
+ }
+
+ @override
+ String? getDescription() {
+ return description;
+ }
+
+ @override
+ String? getLocation() {
+ return location;
+ }
+}
+
+abstract class EventInterface {
+ DateTime getDate();
+ String? getTitle();
+ String? getDescription();
+ String? getLocation();
+ Widget? getIcon();
+ Widget? getDot();
+ int? getId();
+}
diff --git a/packages/flutter_calendar_carousel/lib/classes/event_list.dart b/packages/flutter_calendar_carousel/lib/classes/event_list.dart
new file mode 100644
index 0000000..a8c9252
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/event_list.dart
@@ -0,0 +1,40 @@
+class EventList<T> {
+ Map<DateTime, List<T>> events;
+
+ EventList({
+ required this.events,
+ });
+
+ void add(DateTime date, T event) {
+ final eventsOfDate = events[date];
+ if (eventsOfDate == null)
+ events[date] = [event];
+ else
+ eventsOfDate.add(event);
+ }
+
+ void addAll(DateTime date, List<T> events) {
+ final eventsOfDate = this.events[date];
+ if (eventsOfDate == null)
+ this.events[date] = events;
+ else
+ eventsOfDate.addAll(events);
+ }
+
+ bool remove(DateTime date, T event) {
+ final eventsOfDate = events[date];
+ return eventsOfDate != null ? eventsOfDate.remove(event) : false;
+ }
+
+ List<T> removeAll(DateTime date) {
+ return events.remove(date) ?? [];
+ }
+
+ void clear() {
+ events.clear();
+ }
+
+ List<T> getEvents(DateTime date) {
+ return events[date] ?? [];
+ }
+}
diff --git a/packages/flutter_calendar_carousel/lib/classes/marked_date.dart b/packages/flutter_calendar_carousel/lib/classes/marked_date.dart
new file mode 100644
index 0000000..a3db2d5
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/marked_date.dart
@@ -0,0 +1,46 @@
+import 'package:flutter/material.dart';
+
+class MarkedDate implements MarkedDateInterface {
+ final Color color;
+ final int? id;
+ final TextStyle? textStyle;
+ final DateTime date;
+
+ MarkedDate({
+ required this.color,
+ this.id,
+ this.textStyle,
+ required this.date,
+ });
+
+ @override
+ bool operator ==(dynamic other) {
+ return date == other.date &&
+ color == other.color &&
+ textStyle == other.textStyle &&
+ id == other.id;
+ }
+
+ @override
+ DateTime getDate() => this.date;
+
+ @override
+ int? getId() => this.id;
+
+ @override
+ Color getColor() => this.color;
+
+ @override
+ TextStyle? getTextStyle() => this.textStyle;
+
+ @override
+ // TODO: implement hashCode
+ int get hashCode => super.hashCode;
+}
+
+abstract class MarkedDateInterface {
+ DateTime getDate();
+ Color getColor();
+ int? getId();
+ TextStyle? getTextStyle();
+}
diff --git a/packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart b/packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart
new file mode 100644
index 0000000..3934549
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/classes/multiple_marked_dates.dart
@@ -0,0 +1,86 @@
+import 'marked_date.dart';
+import 'package:flutter/material.dart';
+
+class MultipleMarkedDates {
+ List<MarkedDate> markedDates;
+
+ MultipleMarkedDates({required this.markedDates});
+
+ void add(MarkedDate markedDate) {
+ markedDates.add(markedDate);
+ }
+
+ void addRange(MarkedDate markedDate, {int plus = 0, int minus = 0}) {
+ this.add(markedDate);
+
+ if (plus > 0) {
+ int start = 1;
+ MarkedDate newAddMarkedDate;
+
+ while (start <= plus) {
+ newAddMarkedDate = new MarkedDate(
+ color: markedDate.color,
+ date: markedDate.date.add(Duration(days: start)),
+ textStyle: markedDate.textStyle,
+ );
+
+ this.add(newAddMarkedDate);
+
+ start += 1;
+ }
+ }
+
+ if (minus > 0) {
+ int start = 1;
+ MarkedDate newSubMarkedDate;
+
+ while (start <= minus) {
+ newSubMarkedDate = new MarkedDate(
+ color: markedDate.color,
+ date: markedDate.date.subtract(Duration(days: start)),
+ textStyle: markedDate.textStyle,
+ );
+
+ this.add(newSubMarkedDate);
+
+ start += 1;
+ }
+ }
+ }
+
+ void addAll(List<MarkedDate> markedDates) {
+ this.markedDates.addAll(markedDates);
+ }
+
+ bool remove(MarkedDate markedDate) {
+ return markedDates.remove(markedDate);
+ }
+
+ void clear() {
+ markedDates.clear();
+ }
+
+ bool isMarked(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.date.year == date.year;
+ }
+
+ Color getColor(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.color;
+ }
+
+ DateTime getDate(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.date;
+ }
+
+ TextStyle? getTextStyle(DateTime date) {
+ final results = markedDates.firstWhere((element) => element.date == date,
+ orElse: () => MarkedDate(color: Colors.black, date: DateTime(0)));
+ return results.textStyle;
+ }
+}
diff --git a/packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart b/packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart
new file mode 100644
index 0000000..14e6cd8
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/flutter_calendar_carousel.dart
@@ -0,0 +1,1214 @@
+library flutter_calendar_dooboo;
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_calendar_carousel/classes/event.dart';
+import 'package:flutter_calendar_carousel/classes/event_list.dart';
+import 'package:flutter_calendar_carousel/src/calendar_header.dart';
+import 'package:flutter_calendar_carousel/src/default_styles.dart';
+import 'package:flutter_calendar_carousel/src/weekday_row.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:intl/intl.dart' show DateFormat;
+
+import 'classes/multiple_marked_dates.dart';
+
+export 'package:flutter_calendar_carousel/classes/event_list.dart';
+
+typedef MarkedDateIconBuilder<T> = Widget? Function(T event);
+typedef void OnDayLongPressed(DateTime day);
+
+/// This builder is called for every day in the calendar.
+/// If you want to build only few custom day containers, return null for the days you want to leave with default looks
+/// All characteristics like circle border are also applied to the custom day container [DayBuilder] provides.
+/// (if supplied function returns null, Calendar's function will be called for [day]).
+/// [isSelectable] - is between [CalendarCarousel.minSelectedDate] and [CalendarCarousel.maxSelectedDate]
+/// [index] - DOES NOT equal day number! Index of the day built in current visible field
+/// [isSelectedDay] - if the day is selected
+/// [isToday] - if the day is similar to [DateTime.now()]
+/// [isPrevMonthDay] - if the day is from previous month
+/// [textStyle] - text style that would have been applied by the calendar if it was to build the day.
+/// Example: if the user provided [CalendarCarousel.todayTextStyle] and [isToday] is true,
+/// [CalendarCarousel.todayTextStyle] would be sent into [DayBuilder]'s [textStyle]. If user didn't
+/// provide it, default [CalendarCarousel]'s textStyle would be sent. Same applies to all text styles like
+/// [CalendarCarousel.prevDaysTextStyle], [CalendarCarousel.daysTextStyle] etc.
+/// [isNextMonthDay] - if the day is from next month
+/// [isThisMonthDay] - if the day is from next month
+/// [day] - day being built.
+typedef Widget? DayBuilder(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle textStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime day);
+
+/// This builder is called for every weekday container (7 times, from Mon to Sun).
+/// [weekday] - weekday built, from 0 to 6.
+/// [weekdayName] - string representation of the weekday (Mon, Tue, Wed, etc).
+typedef Widget WeekdayBuilder(int weekday, String weekdayName);
+
+class CalendarCarousel<T extends EventInterface> extends StatefulWidget {
+ final double viewportFraction;
+ final TextStyle? prevDaysTextStyle;
+ final TextStyle? daysTextStyle;
+ final TextStyle? nextDaysTextStyle;
+ final Color prevMonthDayBorderColor;
+ final Color thisMonthDayBorderColor;
+ final Color nextMonthDayBorderColor;
+ final double dayPadding;
+ final double height;
+ final double width;
+ final TextStyle? todayTextStyle;
+ final Color dayButtonColor;
+ final Color todayBorderColor;
+ final Color todayButtonColor;
+ final DateTime? selectedDateTime;
+ final DateTime? targetDateTime;
+ final TextStyle? selectedDayTextStyle;
+ final Color selectedDayButtonColor;
+ final Color selectedDayBorderColor;
+ final bool? daysHaveCircularBorder;
+ final bool disableDayPressed;
+ final Function(DateTime, List<T>)? onDayPressed;
+ final TextStyle? weekdayTextStyle;
+ final Color iconColor;
+ final TextStyle? headerTextStyle;
+ final String? headerText;
+ final TextStyle? weekendTextStyle;
+ final EventList<T>? markedDatesMap;
+
+ /// Change `makredDateWidget` when `markedDateShowIcon` is set to false.
+ final Widget? markedDateWidget;
+
+ /// Change `OutlinedBorder` when `markedDateShowIcon` is set to false.
+ final OutlinedBorder? markedDateCustomShapeBorder;
+
+ /// Change `TextStyle` when `markedDateShowIcon` is set to false.
+ final TextStyle? markedDateCustomTextStyle;
+
+ /// Icon will overlap the [Day] widget when `markedDateShowIcon` is set to true.
+ /// This will also make below parameters work.
+ final bool markedDateShowIcon;
+ final Color? markedDateIconBorderColor;
+ final int markedDateIconMaxShown;
+ final double markedDateIconMargin;
+ final double markedDateIconOffset;
+ final MarkedDateIconBuilder<T>? markedDateIconBuilder;
+
+ /// null - no indicator, true - show the total events, false - show the total of hidden events
+ final bool? markedDateMoreShowTotal;
+ final Decoration? markedDateMoreCustomDecoration;
+ final TextStyle? markedDateMoreCustomTextStyle;
+ final EdgeInsets headerMargin;
+ final double childAspectRatio;
+ final EdgeInsets weekDayMargin;
+ final EdgeInsets weekDayPadding;
+ final WeekdayBuilder? customWeekDayBuilder;
+ final DayBuilder? customDayBuilder;
+ final Color weekDayBackgroundColor;
+ final bool weekFormat;
+ final bool showWeekDays;
+ final bool showHeader;
+ final bool showHeaderButton;
+ final MultipleMarkedDates? multipleMarkedDates;
+ final Widget? leftButtonIcon;
+ final Widget? rightButtonIcon;
+ final ScrollPhysics? customGridViewPhysics;
+ final Function(DateTime)? onCalendarChanged;
+ final String locale;
+ final int? firstDayOfWeek;
+ final DateTime? minSelectedDate;
+ final DateTime? maxSelectedDate;
+ final TextStyle? inactiveDaysTextStyle;
+ final TextStyle? inactiveWeekendTextStyle;
+ final bool headerTitleTouchable;
+ final Function? onHeaderTitlePressed;
+ final Function? onLeftArrowPressed;
+ final Function? onRightArrowPressed;
+ final WeekdayFormat weekDayFormat;
+ final bool staticSixWeekFormat;
+ final bool isScrollable;
+ final Axis scrollDirection;
+ final bool showOnlyCurrentMonthDate;
+ final bool pageSnapping;
+ final OnDayLongPressed? onDayLongPressed;
+ final CrossAxisAlignment dayCrossAxisAlignment;
+ final MainAxisAlignment dayMainAxisAlignment;
+ final bool showIconBehindDayText;
+ final ScrollPhysics pageScrollPhysics;
+ final bool shouldShowTransform;
+
+ CalendarCarousel({
+ Key? key,
+ this.viewportFraction = 1.0,
+ this.prevDaysTextStyle,
+ this.daysTextStyle,
+ this.nextDaysTextStyle,
+ this.prevMonthDayBorderColor = Colors.transparent,
+ this.thisMonthDayBorderColor = Colors.transparent,
+ this.nextMonthDayBorderColor = Colors.transparent,
+ this.dayPadding = 2.0,
+ this.height = double.infinity,
+ this.width = double.infinity,
+ this.todayTextStyle,
+ this.dayButtonColor = Colors.transparent,
+ this.todayBorderColor = Colors.red,
+ this.todayButtonColor = Colors.red,
+ this.selectedDateTime,
+ this.targetDateTime,
+ this.selectedDayTextStyle,
+ this.selectedDayBorderColor = Colors.green,
+ this.selectedDayButtonColor = Colors.green,
+ this.daysHaveCircularBorder,
+ this.disableDayPressed = false,
+ this.onDayPressed,
+ this.weekdayTextStyle = const TextStyle(),
+ this.iconColor = Colors.blueAccent,
+ this.headerTextStyle,
+ this.headerText,
+ this.weekendTextStyle,
+ this.markedDatesMap,
+ this.markedDateShowIcon = false,
+ this.markedDateIconBorderColor,
+ this.markedDateIconMaxShown = 2,
+ this.markedDateIconMargin = 5.0,
+ this.markedDateIconOffset = 5.0,
+ this.markedDateIconBuilder,
+ this.markedDateMoreShowTotal,
+ this.markedDateMoreCustomDecoration,
+ this.markedDateCustomShapeBorder,
+ this.markedDateCustomTextStyle,
+ this.markedDateMoreCustomTextStyle,
+ this.markedDateWidget,
+ this.multipleMarkedDates,
+ this.headerMargin = const EdgeInsets.symmetric(vertical: 16.0),
+ this.childAspectRatio = 1.0,
+ this.weekDayMargin = const EdgeInsets.only(bottom: 4.0),
+ this.weekDayPadding = const EdgeInsets.all(0.0),
+ this.weekDayBackgroundColor = Colors.transparent,
+ this.customWeekDayBuilder,
+ this.customDayBuilder,
+ this.showWeekDays = true,
+ this.weekFormat = false,
+ this.showHeader = true,
+ this.showHeaderButton = true,
+ this.leftButtonIcon,
+ this.rightButtonIcon,
+ this.customGridViewPhysics,
+ this.onCalendarChanged,
+ this.locale = "en",
+ this.firstDayOfWeek,
+ this.minSelectedDate,
+ this.maxSelectedDate,
+ this.inactiveDaysTextStyle,
+ this.inactiveWeekendTextStyle,
+ this.headerTitleTouchable = false,
+ this.onHeaderTitlePressed,
+ this.onLeftArrowPressed,
+ this.onRightArrowPressed,
+ this.weekDayFormat = WeekdayFormat.short,
+ this.staticSixWeekFormat = false,
+ this.isScrollable = true,
+ this.scrollDirection = Axis.horizontal,
+ this.showOnlyCurrentMonthDate = false,
+ this.pageSnapping = false,
+ this.onDayLongPressed,
+ this.dayCrossAxisAlignment = CrossAxisAlignment.center,
+ this.dayMainAxisAlignment = MainAxisAlignment.center,
+ this.showIconBehindDayText = false,
+ this.pageScrollPhysics = const ScrollPhysics(),
+ this.shouldShowTransform = true,
+ }) : super(key: key);
+
+ @override
+ _CalendarState<T> createState() => _CalendarState<T>();
+}
+
+enum WeekdayFormat {
+ weekdays,
+ standalone,
+ short,
+ standaloneShort,
+ narrow,
+ standaloneNarrow,
+}
+
+class _CalendarState<T extends EventInterface>
+ extends State<CalendarCarousel<T>> {
+ late PageController _controller;
+ late List<DateTime> _dates;
+ late List<List<DateTime>> _weeks;
+ DateTime _selectedDate = DateTime.now();
+ late DateTime _targetDate;
+ int _startWeekday = 0;
+ int _endWeekday = 0;
+ late DateFormat _localeDate;
+ int _pageNum = 0;
+ late DateTime minDate;
+ late DateTime maxDate;
+
+ /// When FIRSTDAYOFWEEK is 0 in dart-intl, it represents Monday. However it is the second day in the arrays of Weekdays.
+ /// Therefore we need to add 1 modulo 7 to pick the right weekday from intl. (cf. [GlobalMaterialLocalizations])
+ late int firstDayOfWeek;
+
+ /// If the setState called from this class, don't reload the selectedDate, but it should reload selected date if called from external class
+
+ @override
+ initState() {
+ super.initState();
+ initializeDateFormatting();
+
+ minDate = widget.minSelectedDate ?? DateTime(2018);
+ maxDate = widget.maxSelectedDate ??
+ DateTime(
+ DateTime.now().year + 1, DateTime.now().month, DateTime.now().day);
+
+ final selectedDateTime = widget.selectedDateTime;
+ if (selectedDateTime != null) _selectedDate = selectedDateTime;
+
+ _init();
+
+ /// setup pageController
+ _controller = PageController(
+ initialPage: this._pageNum,
+ keepPage: true,
+ viewportFraction: widget.viewportFraction,
+
+ /// width percentage
+ );
+
+ _localeDate = DateFormat.yMMM(widget.locale);
+ firstDayOfWeek = widget.firstDayOfWeek ??
+ (_localeDate.dateSymbols.FIRSTDAYOFWEEK + 1) % 7;
+
+ _setDate();
+ }
+
+ @override
+ void didUpdateWidget(CalendarCarousel<T> oldWidget) {
+ if (widget.targetDateTime != null && widget.targetDateTime != _targetDate) {
+ _init();
+ _setDate(_pageNum);
+ }
+
+ super.didUpdateWidget(oldWidget);
+ }
+
+ @override
+ dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ _init() {
+ final targetDateTime = widget.targetDateTime;
+ if (targetDateTime != null) {
+ if (targetDateTime.difference(minDate).inDays < 0) {
+ _targetDate = minDate;
+ } else if (targetDateTime.difference(maxDate).inDays > 0) {
+ _targetDate = maxDate;
+ } else {
+ _targetDate = targetDateTime;
+ }
+ } else {
+ _targetDate = _selectedDate;
+ }
+ if (widget.weekFormat) {
+ _pageNum = _targetDate.difference(_firstDayOfWeek(minDate)).inDays ~/ 7;
+ } else {
+ _pageNum = (_targetDate.year - minDate.year) * 12 +
+ _targetDate.month -
+ minDate.month;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final headerText = widget.headerText;
+ return Container(
+ width: widget.width,
+ height: widget.height,
+ child: Column(
+ children: <Widget>[
+ CalendarHeader(
+ showHeader: widget.showHeader,
+ headerMargin: widget.headerMargin,
+ headerTitle: headerText != null
+ ? headerText
+ : widget.weekFormat
+ ? '${_localeDate.format(this._weeks[this._pageNum].first)}'
+ : '${_localeDate.format(this._dates[this._pageNum])}',
+ headerTextStyle: widget.headerTextStyle,
+ showHeaderButtons: widget.showHeaderButton,
+ headerIconColor: widget.iconColor,
+ leftButtonIcon: widget.leftButtonIcon,
+ rightButtonIcon: widget.rightButtonIcon,
+ onLeftButtonPressed: () {
+ widget.onLeftArrowPressed?.call();
+
+ if (this._pageNum > 0) _setDate(this._pageNum - 1);
+ },
+ onRightButtonPressed: () {
+ widget.onRightArrowPressed?.call();
+
+ if (widget.weekFormat) {
+ if (this._weeks.length - 1 > this._pageNum)
+ _setDate(this._pageNum + 1);
+ } else {
+ if (this._dates.length - 1 > this._pageNum)
+ _setDate(this._pageNum + 1);
+ }
+ },
+ onHeaderTitlePressed: widget.headerTitleTouchable
+ ? () {
+ final onHeaderTitlePressed = widget.onHeaderTitlePressed;
+ if (onHeaderTitlePressed != null) {
+ onHeaderTitlePressed();
+ } else {
+ _selectDateFromPicker();
+ }
+ }
+ : null,
+ ),
+ WeekdayRow(
+ firstDayOfWeek,
+ widget.customWeekDayBuilder,
+ showWeekdays: widget.showWeekDays,
+ weekdayFormat: widget.weekDayFormat,
+ weekdayMargin: widget.weekDayMargin,
+ weekdayPadding: widget.weekDayPadding,
+ weekdayBackgroundColor: widget.weekDayBackgroundColor,
+ weekdayTextStyle: widget.weekdayTextStyle,
+ localeDate: _localeDate,
+ ),
+ Expanded(
+ child: PageView.builder(
+ itemCount:
+ widget.weekFormat ? this._weeks.length : this._dates.length,
+ physics: widget.isScrollable
+ ? widget.pageScrollPhysics
+ : NeverScrollableScrollPhysics(),
+ scrollDirection: widget.scrollDirection,
+ onPageChanged: (index) {
+ this._setDate(index);
+ },
+ controller: _controller,
+ itemBuilder: (context, index) {
+ return widget.weekFormat ? weekBuilder(index) : builder(index);
+ },
+ pageSnapping: widget.pageSnapping,
+ )),
+ ],
+ ),
+ );
+ }
+
+ Widget getDefaultDayContainer(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now,
+ ) {
+ return Container(
+ width: double.infinity,
+ height: double.infinity,
+ child: Row(
+ crossAxisAlignment: widget.dayCrossAxisAlignment,
+ mainAxisAlignment: widget.dayMainAxisAlignment,
+ children: <Widget>[
+ DefaultTextStyle(
+ style: getDefaultDayStyle(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay),
+ child: Text(
+ '${now.day}',
+ semanticsLabel: now.day.toString(),
+ style: getDayStyle(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now),
+ maxLines: 1,
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget renderDay(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now,
+ ) {
+ // If day is in Multiple selection mode, get its color
+ bool isMultipleMarked = widget.multipleMarkedDates?.isMarked(now) ?? false;
+ Color? multipleMarkedColor = widget.multipleMarkedDates?.getColor(now);
+
+ final markedDatesMap = widget.markedDatesMap;
+ return Container(
+ margin: EdgeInsets.all(widget.dayPadding),
+ child: GestureDetector(
+ onLongPress: () => _onDayLongPressed(now),
+ child: TextButton(
+ style: TextButton.styleFrom(
+ shape: widget.markedDateCustomShapeBorder != null &&
+ markedDatesMap != null &&
+ markedDatesMap.getEvents(now).length > 0
+ ? widget.markedDateCustomShapeBorder
+ : widget.daysHaveCircularBorder == null
+ ? CircleBorder()
+ : widget.daysHaveCircularBorder ?? false
+ ? CircleBorder(
+ side: BorderSide(
+ color: isSelectedDay
+ ? widget.selectedDayBorderColor
+ : isToday
+ ? widget.todayBorderColor
+ : isPrevMonthDay
+ ? widget.prevMonthDayBorderColor
+ : isNextMonthDay
+ ? widget.nextMonthDayBorderColor
+ : widget.thisMonthDayBorderColor,
+ ),
+ )
+ : RoundedRectangleBorder(
+ side: BorderSide(
+ color: isSelectedDay
+ ? widget.selectedDayBorderColor
+ : isToday
+ ? widget.todayBorderColor
+ : isPrevMonthDay
+ ? widget.prevMonthDayBorderColor
+ : isNextMonthDay
+ ? widget.nextMonthDayBorderColor
+ : widget.thisMonthDayBorderColor,
+ ),
+ ),
+ backgroundColor: isSelectedDay
+ ? widget.selectedDayButtonColor
+ : isToday
+ ? widget.todayButtonColor
+
+ // If day is in Multiple selection mode, apply a different color
+ : isMultipleMarked
+ ? multipleMarkedColor
+ : widget.dayButtonColor,
+ padding: EdgeInsets.all(widget.dayPadding),
+ ),
+ onPressed: widget.disableDayPressed ? null : () => _onDayPressed(now),
+ child: Stack(
+ children: widget.showIconBehindDayText
+ ? <Widget>[
+ widget.markedDatesMap != null
+ ? _renderMarkedMapContainer(now)
+ : Container(),
+ getDayContainer(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now),
+ ]
+ : <Widget>[
+ getDayContainer(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now),
+ widget.markedDatesMap != null
+ ? _renderMarkedMapContainer(now)
+ : Container(),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ AnimatedBuilder builder(int slideIndex) {
+ _startWeekday = _dates[slideIndex].weekday - firstDayOfWeek;
+ if (_startWeekday == 7) {
+ _startWeekday = 0;
+ }
+ _endWeekday =
+ DateTime(_dates[slideIndex].year, _dates[slideIndex].month + 1, 1)
+ .weekday -
+ firstDayOfWeek;
+ double screenWidth = MediaQuery.of(context).size.width;
+ int totalItemCount = widget.staticSixWeekFormat
+ ? 42
+ : DateTime(
+ _dates[slideIndex].year,
+ _dates[slideIndex].month + 1,
+ 0,
+ ).day +
+ _startWeekday +
+ (7 - _endWeekday);
+ int year = _dates[slideIndex].year;
+ int month = _dates[slideIndex].month;
+
+ return AnimatedBuilder(
+ animation: _controller,
+ builder: (context, child) {
+ if (!widget.shouldShowTransform) {
+ return child!;
+ }
+ double value = 1.0;
+ if (_controller.position.haveDimensions) {
+ value = _controller.page! - slideIndex;
+ value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
+ }
+
+ return Center(
+ child: SizedBox(
+ height: Curves.easeOut.transform(value) * widget.height,
+ width: Curves.easeOut.transform(value) * screenWidth,
+ child: child,
+ ),
+ );
+ },
+ child: Stack(
+ children: <Widget>[
+ Positioned(
+ child: Container(
+ width: double.infinity,
+ height: double.infinity,
+ child: GridView.count(
+ physics: widget.customGridViewPhysics,
+ crossAxisCount: 7,
+ childAspectRatio: widget.childAspectRatio,
+ padding: EdgeInsets.zero,
+ children: List.generate(totalItemCount,
+
+ /// last day of month + weekday
+ (index) {
+ final selectedDateTime = widget.selectedDateTime;
+ bool isToday =
+ DateTime.now().day == index + 1 - _startWeekday &&
+ DateTime.now().month == month &&
+ DateTime.now().year == year;
+ bool isSelectedDay = selectedDateTime != null &&
+ selectedDateTime.year == year &&
+ selectedDateTime.month == month &&
+ selectedDateTime.day == index + 1 - _startWeekday;
+ bool isPrevMonthDay = index < _startWeekday;
+ bool isNextMonthDay = index >=
+ (DateTime(year, month + 1, 0).day) + _startWeekday;
+ bool isThisMonthDay = !isPrevMonthDay && !isNextMonthDay;
+
+ DateTime now = DateTime(year, month, 1);
+ TextStyle? textStyle;
+ TextStyle defaultTextStyle;
+ if (isPrevMonthDay && !widget.showOnlyCurrentMonthDate) {
+ now = now.subtract(Duration(days: _startWeekday - index));
+ textStyle = widget.prevDaysTextStyle;
+ defaultTextStyle = defaultPrevDaysTextStyle;
+ } else if (isThisMonthDay) {
+ now = DateTime(year, month, index + 1 - _startWeekday);
+ textStyle = isSelectedDay
+ ? widget.selectedDayTextStyle
+ : isToday
+ ? widget.todayTextStyle
+ : widget.daysTextStyle;
+ defaultTextStyle = isSelectedDay
+ ? defaultSelectedDayTextStyle
+ : isToday
+ ? defaultTodayTextStyle
+ : defaultDaysTextStyle;
+ } else if (!widget.showOnlyCurrentMonthDate) {
+ now = DateTime(year, month, index + 1 - _startWeekday);
+ textStyle = widget.nextDaysTextStyle;
+ defaultTextStyle = defaultNextDaysTextStyle;
+ } else {
+ return Container();
+ }
+ final markedDatesMap = widget.markedDatesMap;
+ if (widget.markedDateCustomTextStyle != null &&
+ markedDatesMap != null &&
+ markedDatesMap.getEvents(now).length > 0) {
+ textStyle = widget.markedDateCustomTextStyle;
+ }
+ bool isSelectable = true;
+ if (now.millisecondsSinceEpoch <
+ minDate.millisecondsSinceEpoch)
+ isSelectable = false;
+ else if (now.millisecondsSinceEpoch >
+ maxDate.millisecondsSinceEpoch) isSelectable = false;
+ return renderDay(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now);
+ }),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ AnimatedBuilder weekBuilder(int slideIndex) {
+ double screenWidth = MediaQuery.of(context).size.width;
+ List<DateTime> weekDays = _weeks[slideIndex];
+
+ weekDays = weekDays
+ .map((weekDay) => weekDay.add(Duration(days: firstDayOfWeek)))
+ .toList();
+
+ return AnimatedBuilder(
+ animation: _controller,
+ builder: (context, child) {
+ double value = 1.0;
+ if (_controller.position.haveDimensions) {
+ value = _controller.page! - slideIndex;
+ value = (1 - (value.abs() * .5)).clamp(0.0, 1.0);
+ }
+
+ return Center(
+ child: SizedBox(
+ height: Curves.easeOut.transform(value) * widget.height,
+ width: Curves.easeOut.transform(value) * screenWidth,
+ child: child,
+ ),
+ );
+ },
+ child: Stack(
+ children: <Widget>[
+ Positioned(
+ child: Container(
+ width: double.infinity,
+ height: double.infinity,
+ child: GridView.count(
+ physics: widget.customGridViewPhysics,
+ crossAxisCount: 7,
+ childAspectRatio: widget.childAspectRatio,
+ padding: EdgeInsets.zero,
+ children: List.generate(weekDays.length, (index) {
+ /// last day of month + weekday
+ bool isToday = weekDays[index].day == DateTime.now().day &&
+ weekDays[index].month == DateTime.now().month &&
+ weekDays[index].year == DateTime.now().year;
+ bool isSelectedDay =
+ this._selectedDate.year == weekDays[index].year &&
+ this._selectedDate.month == weekDays[index].month &&
+ this._selectedDate.day == weekDays[index].day;
+ bool isPrevMonthDay =
+ weekDays[index].month < this._targetDate.month;
+ bool isNextMonthDay =
+ weekDays[index].month > this._targetDate.month;
+ bool isThisMonthDay = !isPrevMonthDay && !isNextMonthDay;
+
+ DateTime now = DateTime(weekDays[index].year,
+ weekDays[index].month, weekDays[index].day);
+ TextStyle? textStyle;
+ TextStyle defaultTextStyle;
+ if (isPrevMonthDay && !widget.showOnlyCurrentMonthDate) {
+ textStyle = widget.prevDaysTextStyle;
+ defaultTextStyle = defaultPrevDaysTextStyle;
+ } else if (isThisMonthDay) {
+ textStyle = isSelectedDay
+ ? widget.selectedDayTextStyle
+ : isToday
+ ? widget.todayTextStyle
+ : widget.daysTextStyle;
+ defaultTextStyle = isSelectedDay
+ ? defaultSelectedDayTextStyle
+ : isToday
+ ? defaultTodayTextStyle
+ : defaultDaysTextStyle;
+ } else if (!widget.showOnlyCurrentMonthDate) {
+ textStyle = widget.nextDaysTextStyle;
+ defaultTextStyle = defaultNextDaysTextStyle;
+ } else {
+ return Container();
+ }
+ bool isSelectable = true;
+ if (now.millisecondsSinceEpoch <
+ minDate.millisecondsSinceEpoch)
+ isSelectable = false;
+ else if (now.millisecondsSinceEpoch >
+ maxDate.millisecondsSinceEpoch) isSelectable = false;
+ return renderDay(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now);
+ }),
+ ),
+ ),
+ ),
+ ],
+ ));
+ }
+
+ List<DateTime> _getDaysInWeek([DateTime? selectedDate]) {
+ if (selectedDate == null) selectedDate = new DateTime.now();
+
+ var firstDayOfCurrentWeek = _firstDayOfWeek(selectedDate);
+ var lastDayOfCurrentWeek = _lastDayOfWeek(selectedDate);
+
+ return _daysInRange(firstDayOfCurrentWeek, lastDayOfCurrentWeek).toList();
+ }
+
+ DateTime _firstDayOfWeek(DateTime date) {
+ var day = _createUTCMiddayDateTime(date);
+ return day.subtract(new Duration(days: date.weekday % 7));
+ }
+
+ DateTime _lastDayOfWeek(DateTime date) {
+ var day = _createUTCMiddayDateTime(date);
+ return day.add(new Duration(days: 7 - day.weekday % 7));
+ }
+
+ DateTime _createUTCMiddayDateTime(DateTime date) {
+ // Magic const: 12 is to maintain compatibility with date_utils
+ return new DateTime.utc(date.year, date.month, date.day, 12, 0, 0);
+ }
+
+ Iterable<DateTime> _daysInRange(DateTime start, DateTime end) {
+ var offset = start.timeZoneOffset;
+
+ return List<int>.generate(end.difference(start).inDays, (i) => i + 1)
+ .map((int i) {
+ var d = start.add(Duration(days: i - 1));
+
+ var timeZoneDiff = d.timeZoneOffset - offset;
+ if (timeZoneDiff.inSeconds != 0) {
+ offset = d.timeZoneOffset;
+ d = d.subtract(new Duration(seconds: timeZoneDiff.inSeconds));
+ }
+ return d;
+ });
+ }
+
+ void _onDayLongPressed(DateTime picked) {
+ widget.onDayLongPressed?.call(picked);
+ }
+
+ void _onDayPressed(DateTime picked) {
+ if (picked.millisecondsSinceEpoch < minDate.millisecondsSinceEpoch) return;
+ if (picked.millisecondsSinceEpoch > maxDate.millisecondsSinceEpoch) return;
+
+ setState(() {
+ _selectedDate = picked;
+ });
+ widget.onDayPressed
+ ?.call(picked, widget.markedDatesMap?.getEvents(picked) ?? const []);
+ }
+
+ Future<Null> _selectDateFromPicker() async {
+ DateTime? selected = await showDatePicker(
+ context: context,
+ initialDate: _selectedDate,
+ firstDate: minDate,
+ lastDate: maxDate,
+ );
+
+ if (selected != null) {
+ // updating selected date range based on selected week
+ setState(() {
+ _selectedDate = selected;
+ });
+ widget.onDayPressed?.call(
+ selected, widget.markedDatesMap?.getEvents(selected) ?? const []);
+ }
+ }
+
+ void _setDatesAndWeeks() {
+ /// Setup default calendar format
+ List<DateTime> date = [];
+ int currentDateIndex = 0;
+ for (int _cnt = 0;
+ 0 >=
+ DateTime(minDate.year, minDate.month + _cnt)
+ .difference(DateTime(maxDate.year, maxDate.month))
+ .inDays;
+ _cnt++) {
+ date.add(DateTime(minDate.year, minDate.month + _cnt, 1));
+ if (0 ==
+ date.last
+ .difference(
+ DateTime(this._targetDate.year, this._targetDate.month))
+ .inDays) {
+ currentDateIndex = _cnt;
+ }
+ }
+
+ /// Setup week-only format
+ List<List<DateTime>> week = [];
+ for (int _cnt = 0;
+ 0 >=
+ minDate
+ .add(Duration(days: 7 * _cnt))
+ .difference(maxDate.add(Duration(days: 7)))
+ .inDays;
+ _cnt++) {
+ week.add(_getDaysInWeek(minDate.add(new Duration(days: 7 * _cnt))));
+ }
+
+ _startWeekday = date[currentDateIndex].weekday - firstDayOfWeek;
+ /*if (widget.showOnlyCurrentMonthDate) {
+ _startWeekday--;
+ }*/
+ if (/*widget.showOnlyCurrentMonthDate && */ _startWeekday == 7) {
+ _startWeekday = 0;
+ }
+ _endWeekday = DateTime(date[currentDateIndex].year,
+ date[currentDateIndex].month + 1, 1)
+ .weekday -
+ firstDayOfWeek;
+ this._dates = date;
+ this._weeks = week;
+// this._selectedDate = widget.selectedDateTime != null
+// ? widget.selectedDateTime
+// : DateTime.now();
+ }
+
+ void _setDate([int page = -1]) {
+ if (page == -1) {
+ setState(() {
+ _setDatesAndWeeks();
+ });
+ } else {
+ if (widget.weekFormat) {
+ setState(() {
+ this._pageNum = page;
+ this._targetDate = this._weeks[page].first;
+ });
+
+ _controller.animateToPage(page,
+ duration: Duration(milliseconds: 1), curve: Threshold(0.0));
+ } else {
+ setState(() {
+ this._pageNum = page;
+ this._targetDate = this._dates[page];
+ _startWeekday = _dates[page].weekday - firstDayOfWeek;
+ _endWeekday = _lastDayOfWeek(_dates[page]).weekday - firstDayOfWeek;
+ });
+ _controller.animateToPage(page,
+ duration: Duration(milliseconds: 1), curve: Threshold(0.0));
+ }
+
+ //call callback
+ final onCalendarChanged = widget.onCalendarChanged;
+ if (onCalendarChanged != null) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ onCalendarChanged(!widget.weekFormat
+ ? this._dates[page]
+ : this._weeks[page][firstDayOfWeek]);
+ });
+ }
+ }
+ }
+
+ Widget _renderMarkedMapContainer(DateTime now) {
+ if (widget.markedDateShowIcon) {
+ return Stack(
+ children: _renderMarkedMap(now),
+ );
+ } else {
+ return Container(
+ height: double.infinity,
+ padding: EdgeInsets.only(bottom: 4.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: _renderMarkedMap(now),
+ ),
+ );
+ }
+ }
+
+ List<Widget> _renderMarkedMap(DateTime now) {
+ final markedEvents = widget.markedDatesMap?.getEvents(now) ?? [];
+ final markedDateIconBuilder = widget.markedDateIconBuilder;
+ final markedDateWidget = widget.markedDateWidget;
+ final markedDateMoreShowTotal = widget.markedDateMoreShowTotal;
+ final markedDateMoreCustomTextStyle = widget.markedDateMoreCustomTextStyle;
+ final markedDateIconMargin = widget.markedDateIconMargin;
+ final markedDateShowIcon = widget.markedDateShowIcon;
+ final markedDateIconMaxShown = widget.markedDateIconMaxShown;
+ final markedDateIconOffset = widget.markedDateIconOffset;
+ final markedDateMoreCustomDecoration =
+ widget.markedDateMoreCustomDecoration;
+
+ if (markedEvents.length > 0) {
+ List<Widget> tmp = [];
+ int count = 0;
+ int eventIndex = 0;
+ double offset = 0.0;
+ double padding = markedDateIconMargin;
+ markedEvents.forEach((T event) {
+ if (markedDateShowIcon) {
+ if (tmp.length > 0 && tmp.length < markedDateIconMaxShown) {
+ offset += markedDateIconOffset;
+ }
+ if (tmp.length < markedDateIconMaxShown &&
+ markedDateIconBuilder != null) {
+ tmp.add(Center(
+ child: new Container(
+ padding: EdgeInsets.only(
+ top: padding + offset,
+ left: padding + offset,
+ right: padding - offset,
+ bottom: padding - offset,
+ ),
+ width: double.infinity,
+ height: double.infinity,
+ child: markedDateIconBuilder(event),
+ )));
+ } else {
+ count++;
+ }
+ if (count > 0 && markedDateMoreShowTotal != null) {
+ tmp.add(
+ Positioned(
+ bottom: 0.0,
+ right: 0.0,
+ child: Container(
+ padding: EdgeInsets.all(4.0),
+ width: markedDateMoreShowTotal ? 18 : null,
+ height: markedDateMoreShowTotal ? 18 : null,
+ decoration: markedDateMoreCustomDecoration == null
+ ? new BoxDecoration(
+ color: Colors.red,
+ borderRadius:
+ BorderRadius.all(Radius.circular(1000.0)),
+ )
+ : markedDateMoreCustomDecoration,
+ child: Center(
+ child: Text(
+ markedDateMoreShowTotal
+ ? (count + markedDateIconMaxShown).toString()
+ : (count.toString() + '+'),
+ semanticsLabel: markedDateMoreShowTotal
+ ? (count + markedDateIconMaxShown).toString()
+ : (count.toString() + '+'),
+ style: markedDateMoreCustomTextStyle == null
+ ? TextStyle(
+ fontSize: 9.0,
+ color: Colors.white,
+ fontWeight: FontWeight.normal)
+ : markedDateMoreCustomTextStyle,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+ } else {
+ //max 5 dots
+ if (eventIndex < 5) {
+ Widget? widget;
+
+ if (markedDateIconBuilder != null) {
+ widget = markedDateIconBuilder(event);
+ }
+
+ if (widget != null) {
+ tmp.add(widget);
+ } else {
+ final dot = event.getDot();
+ if (dot != null) {
+ tmp.add(dot);
+ } else if (markedDateWidget != null) {
+ tmp.add(markedDateWidget);
+ } else {
+ tmp.add(defaultMarkedDateWidget);
+ }
+ }
+ }
+ }
+
+ eventIndex++;
+ });
+ return tmp;
+ }
+ return [];
+ }
+
+ TextStyle getDefaultDayStyle(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ ) {
+ return !isSelectable
+ ? defaultInactiveDaysTextStyle
+ : (_localeDate.dateSymbols.WEEKENDRANGE
+ .contains((index - 1 + firstDayOfWeek) % 7)) &&
+ !isSelectedDay &&
+ !isToday
+ ? (isPrevMonthDay
+ ? defaultPrevDaysTextStyle
+ : isNextMonthDay
+ ? defaultNextDaysTextStyle
+ : isSelectable
+ ? defaultWeekendTextStyle
+ : defaultInactiveWeekendTextStyle)
+ : isToday
+ ? defaultTodayTextStyle
+ : isSelectable && textStyle != null
+ ? textStyle
+ : defaultTextStyle;
+ }
+
+ TextStyle? getDayStyle(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now) {
+ // If day is in multiple selection get its style(if available)
+ bool isMultipleMarked = widget.multipleMarkedDates?.isMarked(now) ?? false;
+ TextStyle? mutipleMarkedTextStyle =
+ widget.multipleMarkedDates?.getTextStyle(now);
+
+ return isSelectedDay && widget.selectedDayTextStyle != null
+ ? widget.selectedDayTextStyle
+ : isMultipleMarked
+ ? mutipleMarkedTextStyle
+ : (_localeDate.dateSymbols.WEEKENDRANGE
+ .contains((index - 1 + firstDayOfWeek) % 7)) &&
+ !isSelectedDay &&
+ isThisMonthDay &&
+ !isToday
+ ? (isSelectable
+ ? widget.weekendTextStyle
+ : widget.inactiveWeekendTextStyle)
+ : !isSelectable
+ ? widget.inactiveDaysTextStyle
+ : isPrevMonthDay
+ ? widget.prevDaysTextStyle
+ : isNextMonthDay
+ ? widget.nextDaysTextStyle
+ : isToday
+ ? widget.todayTextStyle
+ : widget.daysTextStyle;
+ }
+
+ Widget getDayContainer(
+ bool isSelectable,
+ int index,
+ bool isSelectedDay,
+ bool isToday,
+ bool isPrevMonthDay,
+ TextStyle? textStyle,
+ TextStyle defaultTextStyle,
+ bool isNextMonthDay,
+ bool isThisMonthDay,
+ DateTime now) {
+ final customDayBuilder = widget.customDayBuilder;
+
+ Widget? dayContainer;
+ if (customDayBuilder != null) {
+ final appTextStyle = DefaultTextStyle.of(context).style;
+
+ final dayStyle = getDayStyle(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now,
+ );
+
+ final styleForBuilder = appTextStyle.merge(dayStyle);
+
+ dayContainer = customDayBuilder(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ styleForBuilder,
+ isNextMonthDay,
+ isThisMonthDay,
+ now);
+ }
+
+ return dayContainer ??
+ getDefaultDayContainer(
+ isSelectable,
+ index,
+ isSelectedDay,
+ isToday,
+ isPrevMonthDay,
+ textStyle,
+ defaultTextStyle,
+ isNextMonthDay,
+ isThisMonthDay,
+ now,
+ );
+ }
+}
diff --git a/packages/flutter_calendar_carousel/lib/src/calendar_header.dart b/packages/flutter_calendar_carousel/lib/src/calendar_header.dart
new file mode 100644
index 0000000..7fcfd81
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/src/calendar_header.dart
@@ -0,0 +1,73 @@
+import 'package:flutter/material.dart';
+import 'default_styles.dart' show defaultHeaderTextStyle;
+
+class CalendarHeader extends StatelessWidget {
+ /// Passing in values for [leftButtonIcon] or [rightButtonIcon] will override [headerIconColor]
+ CalendarHeader(
+ {required this.headerTitle,
+ this.headerMargin,
+ required this.showHeader,
+ this.headerTextStyle,
+ this.showHeaderButtons = true,
+ this.headerIconColor,
+ this.leftButtonIcon,
+ this.rightButtonIcon,
+ required this.onLeftButtonPressed,
+ required this.onRightButtonPressed,
+ this.onHeaderTitlePressed})
+ : isTitleTouchable = onHeaderTitlePressed != null;
+
+ final String headerTitle;
+ final EdgeInsetsGeometry? headerMargin;
+ final bool showHeader;
+ final TextStyle? headerTextStyle;
+ final bool showHeaderButtons;
+ final Color? headerIconColor;
+ final Widget? leftButtonIcon;
+ final Widget? rightButtonIcon;
+ final VoidCallback onLeftButtonPressed;
+ final VoidCallback onRightButtonPressed;
+ final bool isTitleTouchable;
+ final VoidCallback? onHeaderTitlePressed;
+
+ TextStyle get getTextStyle => headerTextStyle ?? defaultHeaderTextStyle;
+
+ Widget _leftButton() => IconButton(
+ onPressed: onLeftButtonPressed,
+ icon:
+ leftButtonIcon ?? Icon(Icons.chevron_left, color: headerIconColor),
+ );
+
+ Widget _rightButton() => IconButton(
+ onPressed: onRightButtonPressed,
+ icon: rightButtonIcon ??
+ Icon(Icons.chevron_right, color: headerIconColor),
+ );
+
+ Widget _headerTouchable() => TextButton(
+ onPressed: onHeaderTitlePressed,
+ child: Text(
+ headerTitle,
+ semanticsLabel: headerTitle,
+ style: getTextStyle,
+ ),
+ );
+
+ @override
+ Widget build(BuildContext context) => showHeader
+ ? Container(
+ margin: headerMargin,
+ child: DefaultTextStyle(
+ style: getTextStyle,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: <Widget>[
+ showHeaderButtons ? _leftButton() : Container(),
+ isTitleTouchable
+ ? _headerTouchable()
+ : Text(headerTitle, style: getTextStyle),
+ showHeaderButtons ? _rightButton() : Container(),
+ ])),
+ )
+ : Container();
+}
diff --git a/packages/flutter_calendar_carousel/lib/src/default_styles.dart b/packages/flutter_calendar_carousel/lib/src/default_styles.dart
new file mode 100644
index 0000000..cfeddc9
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/src/default_styles.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+
+const TextStyle defaultHeaderTextStyle = const TextStyle(
+ fontSize: 20.0,
+ color: Colors.blue,
+);
+const TextStyle defaultPrevDaysTextStyle = const TextStyle(
+ color: Colors.grey,
+ fontSize: 14.0,
+);
+const TextStyle defaultNextDaysTextStyle = const TextStyle(
+ color: Colors.grey,
+ fontSize: 14.0,
+);
+const TextStyle defaultDaysTextStyle = const TextStyle(
+ color: Colors.black,
+ fontSize: 14.0,
+);
+const TextStyle defaultTodayTextStyle = const TextStyle(
+ color: Colors.white,
+ fontSize: 14.0,
+);
+const TextStyle defaultSelectedDayTextStyle = const TextStyle(
+ color: Colors.white,
+ fontSize: 14.0,
+);
+const TextStyle defaultWeekdayTextStyle = const TextStyle(
+ color: Colors.deepOrange,
+ fontSize: 14.0,
+);
+const TextStyle defaultWeekendTextStyle = const TextStyle(
+ color: Colors.pinkAccent,
+ fontSize: 14.0,
+);
+const TextStyle defaultInactiveDaysTextStyle = const TextStyle(
+ color: Colors.black38,
+ fontSize: 14.0,
+);
+final TextStyle defaultInactiveWeekendTextStyle = TextStyle(
+ color: Colors.pinkAccent.withOpacity(0.6),
+ fontSize: 14.0,
+);
+final Widget defaultMarkedDateWidget = Container(
+ margin: EdgeInsets.symmetric(horizontal: 1.0),
+ color: Colors.blueAccent,
+ height: 4.0,
+ width: 4.0,
+);
diff --git a/packages/flutter_calendar_carousel/lib/src/weekday_row.dart b/packages/flutter_calendar_carousel/lib/src/weekday_row.dart
new file mode 100644
index 0000000..48f5b3a
--- /dev/null
+++ b/packages/flutter_calendar_carousel/lib/src/weekday_row.dart
@@ -0,0 +1,131 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart';
+import 'package:flutter_calendar_carousel/src/default_styles.dart'
+ show defaultWeekdayTextStyle;
+import 'package:intl/intl.dart';
+
+class WeekdayRow extends StatelessWidget {
+ WeekdayRow(this.firstDayOfWeek, this.customWeekdayBuilder,
+ {required this.showWeekdays,
+ required this.weekdayFormat,
+ required this.weekdayMargin,
+ required this.weekdayPadding,
+ required this.weekdayBackgroundColor,
+ required this.weekdayTextStyle,
+ required this.localeDate});
+
+ final WeekdayBuilder? customWeekdayBuilder;
+ final bool showWeekdays;
+ final WeekdayFormat weekdayFormat;
+ final EdgeInsets weekdayMargin;
+ final EdgeInsets weekdayPadding;
+ final Color weekdayBackgroundColor;
+ final TextStyle? weekdayTextStyle;
+ final DateFormat localeDate;
+ final int firstDayOfWeek;
+
+ Widget _weekdayContainer(int weekday, String weekDayName) {
+ final customWeekdayBuilder = this.customWeekdayBuilder;
+ return customWeekdayBuilder != null
+ ? customWeekdayBuilder(weekday, weekDayName)
+ : Expanded(
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(color: weekdayBackgroundColor),
+ color: weekdayBackgroundColor,
+ ),
+ margin: weekdayMargin,
+ padding: weekdayPadding,
+ child: Center(
+ child: DefaultTextStyle(
+ style: defaultWeekdayTextStyle,
+ child: Text(
+ weekDayName,
+ semanticsLabel: weekDayName,
+ style: weekdayTextStyle,
+ ),
+ ),
+ ),
+ ));
+ }
+
+// List<Widget> _generateWeekdays() {
+// switch (weekdayFormat) {
+// case WeekdayFormat.weekdays:
+// return localeDate.dateSymbols.WEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.standalone:
+// return localeDate.dateSymbols.STANDALONEWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.short:
+// return localeDate.dateSymbols.SHORTWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.standaloneShort:
+// return localeDate.dateSymbols.STANDALONESHORTWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.narrow:
+// return localeDate.dateSymbols.NARROWWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// case WeekdayFormat.standaloneNarrow:
+// return localeDate.dateSymbols.STANDALONENARROWWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// default:
+// return localeDate.dateSymbols.STANDALONEWEEKDAYS
+// .map<Widget>(_weekdayContainer)
+// .toList();
+// }
+// }
+
+ // TODO - locale issues
+ List<Widget> _renderWeekDays() {
+ List<Widget> list = [];
+
+ /// because of number of days in a week is 7, so it would be easier to count it til 7.
+ for (var i = firstDayOfWeek, count = 0;
+ count < 7;
+ i = (i + 1) % 7, count++) {
+ String weekDay;
+
+ switch (weekdayFormat) {
+ case WeekdayFormat.weekdays:
+ weekDay = localeDate.dateSymbols.WEEKDAYS[i];
+ break;
+ case WeekdayFormat.standalone:
+ weekDay = localeDate.dateSymbols.STANDALONEWEEKDAYS[i];
+ break;
+ case WeekdayFormat.short:
+ weekDay = localeDate.dateSymbols.SHORTWEEKDAYS[i];
+ break;
+ case WeekdayFormat.standaloneShort:
+ weekDay = localeDate.dateSymbols.STANDALONESHORTWEEKDAYS[i];
+ break;
+ case WeekdayFormat.narrow:
+ weekDay = localeDate.dateSymbols.NARROWWEEKDAYS[i];
+ break;
+ case WeekdayFormat.standaloneNarrow:
+ weekDay = localeDate.dateSymbols.STANDALONENARROWWEEKDAYS[i];
+ break;
+ default:
+ weekDay = localeDate.dateSymbols.STANDALONEWEEKDAYS[i];
+ break;
+ }
+ list.add(_weekdayContainer(count, weekDay));
+ }
+
+ return list;
+ }
+
+ @override
+ Widget build(BuildContext context) => showWeekdays
+ ? Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: _renderWeekDays(),
+ )
+ : Container();
+}