diff --git a/example/lib/pages/calendar_page.dart b/example/lib/pages/calendar_page.dart index ac79eda..2e7c6bb 100644 --- a/example/lib/pages/calendar_page.dart +++ b/example/lib/pages/calendar_page.dart @@ -109,6 +109,7 @@ class _CalendarPageState extends State { onDayClicked: _showDayEventsInModalSheet, minDate: DateTime.now().subtract(const Duration(days: 1000)), maxDate: DateTime.now().add(const Duration(days: 180)), + //localizedWeekDaysBuilder: (weekDay) => LocalizedWeekDaysWidget(weekDay: weekDay), ), ), ], diff --git a/example/lib/widgets/localized_week_days_widget.dart b/example/lib/widgets/localized_week_days_widget.dart new file mode 100644 index 0000000..d1aa963 --- /dev/null +++ b/example/lib/widgets/localized_week_days_widget.dart @@ -0,0 +1,35 @@ +import 'package:cr_calendar/cr_calendar.dart'; +import 'package:cr_calendar_example/res/colors.dart'; +import 'package:flutter/material.dart'; + +/// Widget that represents week days in row above calendar month view. +class LocalizedWeekDaysWidget extends StatelessWidget { + const LocalizedWeekDaysWidget({ + required this.weekDay, + super.key, + }); + + /// [String] value from [LocalizedWeekDaysBuilder]. + final String weekDay; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.transparent, + width: 0.3, + ), + ), + height: 40, + child: Center( + child: Text( + weekDay, + style: TextStyle( + color: violet.withOpacity(0.9), + ), + ), + ), + ); + } +} diff --git a/lib/src/contract.dart b/lib/src/contract.dart index d689fd4..5f772df 100644 --- a/lib/src/contract.dart +++ b/lib/src/contract.dart @@ -19,4 +19,6 @@ class Contract { static const kYearsInLine = 3; static const kDefaultInitialPage = 4000; + + static const kDefaultDayItemBorderWidth = 0.5; } diff --git a/lib/src/cr_calendar.dart b/lib/src/cr_calendar.dart index 3d122af..23c6a8b 100644 --- a/lib/src/cr_calendar.dart +++ b/lib/src/cr_calendar.dart @@ -4,7 +4,6 @@ import 'package:cr_calendar/src/extensions/datetime_ext.dart'; import 'package:cr_calendar/src/models/calendar_event_model.dart'; import 'package:cr_calendar/src/month_item.dart'; import 'package:cr_calendar/src/utils/debouncer.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; @@ -217,6 +216,7 @@ class CrCalendar extends StatefulWidget { this.onSwipeCallbackDebounceMs = 100, this.minDate, this.maxDate, + this.localizedWeekDaysBuilder, super.key, }) : assert(maxEventLines <= 6, 'maxEventLines should be less then 6'), assert(minDate == null || maxDate == null || minDate.isBefore(maxDate), @@ -279,6 +279,21 @@ class CrCalendar extends StatefulWidget { /// Reduces number of callbacks when [CrCalendarController] goToDate is used. final int onSwipeCallbackDebounceMs; + /// Builder function for week day customization at the top of the calendar. + /// + /// When this parameter is not null, it will be called with each week days first + /// letter as a String, eg. S for Sunday, M for Monday, etc. + /// + /// When this parameter is not null, the first day of the week is determined + /// by the app's current locale, which is en_US in Flutter by default. + /// + /// The week day names will be translated for the current locale as well, + /// eg. if the current locale is German, then M for Montag, D for Dienstag etc. + /// + /// When this parameter is not null, [firstDayOfWeek] and [weekDaysBuilder] + /// parameters are ignored. + final LocalizedWeekDaysBuilder? localizedWeekDaysBuilder; + @override _CrCalendarState createState() => _CrCalendarState(); } @@ -290,6 +305,8 @@ class _CrCalendarState extends State { final _minPage = 1; + late WeekDay _firstWeekDay; + @override void initState() { super.initState(); @@ -311,6 +328,7 @@ class _CrCalendarState extends State { @override Widget build(BuildContext context) { + _initializeFirstDayOfWeek(); return OrientationBuilder( builder: (BuildContext context, Orientation orientation) { return PageView.builder( @@ -354,7 +372,8 @@ class _CrCalendarState extends State { }, weekDaysBuilder: widget.weekDaysBuilder, dayItemBuilder: widget.dayItemBuilder, - firstWeekDay: widget.firstDayOfWeek, + firstWeekDay: _firstWeekDay, + localizedWeekDaysBuilder: widget.localizedWeekDaysBuilder, ), ); }, @@ -439,4 +458,14 @@ class _CrCalendarState extends State { widget.controller.page; } } + + void _initializeFirstDayOfWeek() { + final localizations = MaterialLocalizations.of(context); + + if (widget.localizedWeekDaysBuilder != null) { + _firstWeekDay = WeekDay.values[localizations.firstDayOfWeekIndex]; + } else { + _firstWeekDay = widget.firstDayOfWeek; + } + } } diff --git a/lib/src/customization/builders.dart b/lib/src/customization/builders.dart index 0f31d67..a8f3844 100644 --- a/lib/src/customization/builders.dart +++ b/lib/src/customization/builders.dart @@ -24,3 +24,8 @@ typedef PickerButtonBuilder = Widget? Function(Function? onPress); /// Callback for getting date range data from [CrDatePickerDialog]. typedef OnDateRangeSelected = void Function( DateTime? rangeBegin, DateTime? rangeEnd); + +/// Builder for week day customization at the top of the calendar widget. +/// The weekdays first letter in uppercase is passed as a String, +/// eg. M (Monday), T (Tuesday) etc. +typedef LocalizedWeekDaysBuilder = Widget Function(String weekDay); diff --git a/lib/src/month_item.dart b/lib/src/month_item.dart index 9746ad1..22580ad 100644 --- a/lib/src/month_item.dart +++ b/lib/src/month_item.dart @@ -6,7 +6,6 @@ import 'package:cr_calendar/src/models/event_count_keeper.dart'; import 'package:cr_calendar/src/month_calendar_widget.dart'; import 'package:cr_calendar/src/utils/event_utils.dart'; import 'package:cr_calendar/src/widgets/default_weekday_widget.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; @@ -29,6 +28,7 @@ class MonthItem extends StatefulWidget { this.eventTopPadding = 0, this.onDayTap, this.firstWeekDay = WeekDay.sunday, + this.localizedWeekDaysBuilder, super.key, }); @@ -46,6 +46,7 @@ class MonthItem extends StatefulWidget { final double? eventTopPadding; final TouchMode touchMode; final WeekDay firstWeekDay; + final LocalizedWeekDaysBuilder? localizedWeekDaysBuilder; @override MonthItemState createState() => MonthItemState(); @@ -114,9 +115,13 @@ class MonthItemState extends State { return Column( mainAxisSize: MainAxisSize.min, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: _getDaysOfWeek(), + LayoutBuilder( + builder: (context, constraint) { + final size = _getConstrainedSize(constraint); + return Row( + children: _getDaysOfWeek(size.width), + ); + }, ), Expanded( child: LayoutBuilder( @@ -212,15 +217,41 @@ class MonthItemState extends State { } /// Returns list of week days representation - List _getDaysOfWeek() { + List _getDaysOfWeek(double itemWidth) { + if (widget.localizedWeekDaysBuilder != null) { + return _getLocalizedDaysOfWeek(itemWidth); + } + final sortedWeekDays = sortWeekdays(widget.firstWeekDay); final week = List.generate(WeekDay.values.length, (index) { - final sortedWeekDays = sortWeekdays(widget.firstWeekDay); - return widget.weekDaysBuilder?.call(sortedWeekDays[index]) ?? - DefaultWeekdayWidget(day: sortedWeekDays[index]); + return Container( + width: itemWidth, + child: widget.weekDaysBuilder?.call(sortedWeekDays[index]) ?? + DefaultWeekdayWidget(day: sortedWeekDays[index]), + ); }); return week; } + List _getLocalizedDaysOfWeek(double itemWidth) { + final localizations = MaterialLocalizations.of(context); + final result = []; + + var i = localizations.firstDayOfWeekIndex; + while (true) { + final weekday = localizations.narrowWeekdays[i]; + result.add(Container( + width: itemWidth, + child: widget.localizedWeekDaysBuilder!.call(weekday), + )); + if (i == (localizations.firstDayOfWeekIndex - 1) % 7) { + break; + } + i = (i + 1) % 7; + } + + return result; + } + /// Returns list of events for current month List _calculateWeeks() { final begin = _beginRange; diff --git a/lib/src/widgets/day_item.dart b/lib/src/widgets/day_item.dart index dbd7a2c..38e13af 100644 --- a/lib/src/widgets/day_item.dart +++ b/lib/src/widgets/day_item.dart @@ -1,3 +1,4 @@ +import 'package:cr_calendar/src/contract.dart'; import 'package:flutter/material.dart'; ///Represent calendar day body @@ -27,7 +28,10 @@ class DayItem extends StatelessWidget { Container( alignment: Alignment.topCenter, decoration: BoxDecoration( - border: Border.all(width: 0.5, color: Colors.black12), + border: Border.all( + width: Contract.kDefaultDayItemBorderWidth, + color: Colors.black12, + ), color: isSelectedDay || isWithinRange ? Colors.black12 : null, ), child: Column( diff --git a/lib/src/widgets/default_weekday_widget.dart b/lib/src/widgets/default_weekday_widget.dart index 3f8675d..8ae09b3 100644 --- a/lib/src/widgets/default_weekday_widget.dart +++ b/lib/src/widgets/default_weekday_widget.dart @@ -1,3 +1,4 @@ +import 'package:cr_calendar/src/contract.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -14,6 +15,11 @@ class DefaultWeekdayWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container( + decoration: BoxDecoration( + border: Border.all( + width: Contract.kDefaultDayItemBorderWidth, + color: Colors.transparent), + ), height: 40, child: Center( child: Text(