diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cdac41..1085ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 7.5.25 +- Added the `firstDay` and `lastDay` parameters to `ThemedCalendar` to allow setting a date range for the picker, preventing selection of dates outside the specified range. +- The `firstDay` and `lastDay` parameters are now supported in `ThemedDateRangePicker`, `ThemedDatePicker`, `ThemedDateTimeRangePicker`, `ThemedDateTimeSteppedPicker` and `ThemedDateTimePicker`. + ## 7.5.24 - Fixed `ThemedTable2` silent update loss for mid-list edits: reverted `didUpdateWidget` heuristic back to `DeepCollectionEquality` — the heuristic (identical + length + first element) missed edits to non-first elements in same-length lists; with Freezed value-equality objects the O(n) cost is negligible in practice (~4 of 170 telemetry updates actually triggered a reload in production profiling). diff --git a/example/lib/views/inputs/src/selectors/datetime.dart b/example/lib/views/inputs/src/selectors/datetime.dart index fa73af5..b1cc22f 100644 --- a/example/lib/views/inputs/src/selectors/datetime.dart +++ b/example/lib/views/inputs/src/selectors/datetime.dart @@ -170,6 +170,8 @@ class _DateTimePickersViewState extends State { ThemedDateTimeSteppedPicker( labelText: "Example label", value: _selectedDateTime, + firstDay: DateTime.now().subtract(const Duration(days: 10)), + lastDay: DateTime.now(), onChanged: (val) { _selectedTime = TimeOfDay(hour: val.hour, minute: val.minute); setState(() => _selectedDateTime = val); diff --git a/example/pubspec.lock b/example/pubspec.lock index 978c273..d3b5fbd 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -390,7 +390,7 @@ packages: path: ".." relative: true source: path - version: "7.5.23" + version: "7.5.25" leak_tracker: dependency: transitive description: diff --git a/lib/src/inputs/src/pickers/date/range.dart b/lib/src/inputs/src/pickers/date/range.dart index 1daa42c..fb59784 100644 --- a/lib/src/inputs/src/pickers/date/range.dart +++ b/lib/src/inputs/src/pickers/date/range.dart @@ -81,6 +81,12 @@ class ThemedDateRangePicker extends StatefulWidget { /// [emptyListText] is the text to be displayed when the list is empty. final EdgeInsets? padding; + /// [lastDay] any datetime after this day will be disabled. If null, the calendar will not have a limit. + final DateTime? lastDay; + + /// [firstDay] any datetime before this day will be disabled. If null, the calendar will not have a limit. + final DateTime? firstDay; + /// [ThemedDateRangePicker] is a date picker input. It is a wrapper of [ThemedTextInput] with a date picker. const ThemedDateRangePicker({ super.key, @@ -112,6 +118,8 @@ class ThemedDateRangePicker extends StatefulWidget { this.errors = const [], this.hideDetails = false, this.padding, + this.firstDay, + this.lastDay, }) : assert((label == null && labelText != null) || (label != null && labelText == null)), assert(value.length == 0 || value.length == 2); @@ -220,6 +228,8 @@ class _ThemedDateRangePickerState extends State { smallWeekdays: true, todayIndicator: false, todayButton: false, + firstDay: widget.firstDay, + lastDay: widget.lastDay, onDayTap: (day) { if (tempDate == null) { tempDate = day; diff --git a/lib/src/inputs/src/pickers/date/single.dart b/lib/src/inputs/src/pickers/date/single.dart index 1804acc..00853ac 100644 --- a/lib/src/inputs/src/pickers/date/single.dart +++ b/lib/src/inputs/src/pickers/date/single.dart @@ -84,6 +84,12 @@ class ThemedDatePicker extends StatefulWidget { /// [emptyListText] is the text to be displayed when the list is empty. final EdgeInsets? padding; + /// [lastDay] any datetime after this day will be disabled. If null, the calendar will not have a limit. + final DateTime? lastDay; + + /// [firstDay] any datetime before this day will be disabled. If null, the calendar will not have a limit. + final DateTime? firstDay; + /// [ThemedDatePicker] is a date picker input. It is a wrapper of [ThemedTextInput] with a date picker. const ThemedDatePicker({ super.key, @@ -116,6 +122,8 @@ class ThemedDatePicker extends StatefulWidget { this.errors = const [], this.hideDetails = false, this.padding, + this.firstDay, + this.lastDay, }) : assert((label == null && labelText != null) || (label != null && labelText == null)); @override @@ -199,6 +207,8 @@ class _ThemedDatePickerState extends State { showEntries: false, smallWeekdays: true, disabledDays: widget.disabledDays, + firstDay: widget.firstDay, + lastDay: widget.lastDay, onDayTap: (day) { Navigator.of(context).pop(day); }, diff --git a/lib/src/inputs/src/pickers/datetime/range.dart b/lib/src/inputs/src/pickers/datetime/range.dart index 2a0360d..644305d 100644 --- a/lib/src/inputs/src/pickers/datetime/range.dart +++ b/lib/src/inputs/src/pickers/datetime/range.dart @@ -103,6 +103,12 @@ class ThemedDateTimeRangePicker extends StatefulWidget { /// [emptyListText] is the text to be displayed when the list is empty. final EdgeInsets? padding; + /// [lastDay] any datetime after this day will be disabled. If null, the calendar will not have a limit. + final DateTime? lastDay; + + /// [firstDay] any datetime before this day will be disabled. If null, the calendar will not have a limit. + final DateTime? firstDay; + /// [ThemedDateTimeRangePicker] is a date time picker input. It is a wrapper of [ThemedTextInput] /// with a date time picker. const ThemedDateTimeRangePicker({ @@ -148,6 +154,8 @@ class ThemedDateTimeRangePicker extends StatefulWidget { this.errors = const [], this.hideDetails = false, this.padding, + this.firstDay, + this.lastDay, }) : assert((label == null && labelText != null) || (label != null && labelText == null)), assert(value.length == 0 || value.length == 2); @@ -245,6 +253,8 @@ class _ThemedDateTimeRangePickerState extends State w translations: widget.translations, overridesLayrzTranslations: widget.overridesLayrzTranslations, use24HourFormat: widget.use24HourFormat, + firstDay: widget.firstDay, + lastDay: widget.lastDay, ), ); @@ -276,6 +286,8 @@ class ThemedDateTimeRangeDialog extends StatefulWidget { final List disabledDays; final Map translations; final bool overridesLayrzTranslations; + final DateTime? firstDay; + final DateTime? lastDay; final bool use24HourFormat; const ThemedDateTimeRangeDialog({ @@ -286,6 +298,8 @@ class ThemedDateTimeRangeDialog extends StatefulWidget { this.translations = const {}, this.overridesLayrzTranslations = false, this.use24HourFormat = false, + this.firstDay, + this.lastDay, }); @override @@ -383,6 +397,8 @@ class _ThemedDateTimeRangeDialogState extends State w controller: _tabController, children: [ ThemedCalendar( + firstDay: widget.firstDay, + lastDay: widget.lastDay, focusDay: tempDate, focusOnHighlightedDays: tempDate == null, showEntries: false, diff --git a/lib/src/inputs/src/pickers/datetime/single.dart b/lib/src/inputs/src/pickers/datetime/single.dart index a8573ff..910c1fa 100644 --- a/lib/src/inputs/src/pickers/datetime/single.dart +++ b/lib/src/inputs/src/pickers/datetime/single.dart @@ -103,6 +103,12 @@ class ThemedDateTimePicker extends StatefulWidget { /// [emptyListText] is the text to be displayed when the list is empty. final EdgeInsets? padding; + /// [lastDay] any datetime after this day will be disabled. If null, the calendar will not have a limit. + final DateTime? lastDay; + + /// [firstDay] any datetime before this day will be disabled. If null, the calendar will not have a limit. + final DateTime? firstDay; + /// [ThemedDateTimePicker] is a date time picker input. It is a wrapper of [ThemedTextInput] with a date time picker. const ThemedDateTimePicker({ super.key, @@ -147,6 +153,8 @@ class ThemedDateTimePicker extends StatefulWidget { this.errors = const [], this.hideDetails = false, this.padding, + this.firstDay, + this.lastDay, }) : assert((label == null && labelText != null) || (label != null && labelText == null)); @override @@ -304,6 +312,8 @@ class _ThemedDateTimePickerState extends State with Single disabledDays: widget.disabledDays, translations: widget.translations, overridesLayrzTranslations: widget.overridesLayrzTranslations, + firstDay: widget.firstDay, + lastDay: widget.lastDay, onDayTap: (newDate) => setState(() => date = newDate), ), Column( diff --git a/lib/src/inputs/src/pickers/datetime/single_stepped.dart b/lib/src/inputs/src/pickers/datetime/single_stepped.dart index fb3e160..ebc3ba9 100644 --- a/lib/src/inputs/src/pickers/datetime/single_stepped.dart +++ b/lib/src/inputs/src/pickers/datetime/single_stepped.dart @@ -105,6 +105,12 @@ class ThemedDateTimeSteppedPicker extends StatefulWidget { final bool disableTimePickerBlink; + /// [lastDay] any datetime after this day will be disabled. If null, the calendar will not have a limit. + final DateTime? lastDay; + + /// [firstDay] any datetime before this day will be disabled. If null, the calendar will not have a limit. + final DateTime? firstDay; + /// [ThemedDateTimeSteppedPicker] is a date time stepped picker input. It is a wrapper of [ThemedTextInput] with a date time stepped picker. const ThemedDateTimeSteppedPicker({ super.key, @@ -150,6 +156,8 @@ class ThemedDateTimeSteppedPicker extends StatefulWidget { this.hideDetails = false, this.padding, this.disableTimePickerBlink = false, + this.firstDay, + this.lastDay, }) : assert((label == null && labelText != null) || (label != null && labelText == null)); @override @@ -241,6 +249,8 @@ class _ThemedDateTimeSteppedPickerState extends State { }) .contains(now); - if (widget.isHighlightDaysAsRange) { - isFocusDay = false; - isDisabled = false; + if (widget.firstDay != null) { + isDisabled = + isDisabled || + now.isBefore( + DateTime( + widget.firstDay!.year, + widget.firstDay!.month, + widget.firstDay!.day, + ), + ); } + if (widget.lastDay != null) { + isDisabled = + isDisabled || + now.isAfter( + DateTime( + widget.lastDay!.year, + widget.lastDay!.month, + widget.lastDay!.day, + ), + ); + } + + // if (widget.isHighlightDaysAsRange) { + // isFocusDay = false; + // isDisabled = false; + // } bool isCurrentMonth = day.month == _dayGenerator.month; diff --git a/pubspec.yaml b/pubspec.yaml index 2b45bc4..9fb90e9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: layrz_theme description: Layrz standard styling library for Flutter. Widget library following the Material Design 3 guidelines, with a focus on reliavility and functionality. -version: "7.5.24" +version: "7.5.25" homepage: https://theme.layrz.com repository: https://github.com/goldenm-software/layrz_theme