Skip to content

Commit

Permalink
Merge pull request #19 from hasanmhallak/dev
Browse files Browse the repository at this point in the history
fix initial date assertions close #18 & fix initial page view scrolling issue
  • Loading branch information
hasanmhallak authored Mar 23, 2024
2 parents 2b25a72 + f97982e commit 129fd41
Show file tree
Hide file tree
Showing 17 changed files with 415 additions and 50 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 3.0.1

- Fix Assertions issue see [#18](https://github.com/hasanmhallak/date_picker/issues/18)

# 3.0.0 [Breaking]

- Fix Assertions issue see [#14](https://github.com/hasanmhallak/date_picker/issues/14)
Expand All @@ -9,7 +13,6 @@
- User now can pick the same date as a range fixes [#10](https://github.com/hasanmhallak/date_picker/issues/10)
- User now can choose to center the leading date or not fixes [#10](https://github.com/hasanmhallak/date_picker/issues/10)


# 2.0.3

- Add `cupertino_icons` to dependencies. Closes [#9](https://github.com/hasanmhallak/date_picker/issues/9)
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ To use the Date Picker library, add the following dependency to your `pubspec.ya

```yaml
dependencies:
date_picker_plus: ^3.0.0

date_picker_plus: ^3.0.1
```
Import the library in your Dart file:
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ packages:
path: ".."
relative: true
source: path
version: "2.0.3"
version: "3.0.0"
fake_async:
dependency: transitive
description:
Expand Down
40 changes: 31 additions & 9 deletions lib/src/date/date_picker.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:date_picker_plus/src/shared/utils.dart';
import 'package:flutter/material.dart';

import '../shared/picker_type.dart';
Expand All @@ -24,9 +25,13 @@ import 'show_date_picker_dialog.dart';
class DatePicker extends StatefulWidget {
/// Creates a calendar date picker.
///
/// It will display a grid of days for the [initialDate]'s month. if that
/// is null, `DateTime.now()` will be used. The day
/// indicated by [selectedDate] will be selected if provided.
/// It will display a grid of days for the [initialDate]'s month. If [initialDate]
/// is null, `DateTime.now()` will be used. If `DateTime.now()` does not fall within
/// the valid range of [minDate] and [maxDate], it will fall back to the nearest
/// valid date from `DateTime.now()`, selecting the [maxDate] if `DateTime.now()` is
/// after the valid range, or [minDate] if before.
///
/// The day indicated by [selectedDate] will be selected if provided.
///
/// The optional [onDateSelected] callback will be called if provided when a date
/// is selected.
Expand Down Expand Up @@ -75,8 +80,11 @@ class DatePicker extends StatefulWidget {
assert(!minDate.isAfter(maxDate), "minDate can't be after maxDate");
}

/// The date which will be displayed on first opening.
/// If not specified, the picker will default to `DateTime.now()` date.
/// The date which will be displayed on first opening. If not specified, the picker
/// will default to `DateTime.now()`. If `DateTime.now()` does not fall within the
/// valid range of [minDate] and [maxDate], it will automatically adjust to the nearest
/// valid date, selecting [maxDate] if `DateTime.now()` is after the valid range, or
/// [minDate] if it is before.
///
/// Note that only dates are considered. time fields are ignored.
final DateTime? initialDate;
Expand Down Expand Up @@ -213,7 +221,8 @@ class _DatePickerState extends State<DatePicker> {

@override
void initState() {
_displayedDate = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedDate = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);
_pickerType = widget.initialPickerType;

_selectedDate = widget.selectedDate != null ? DateUtils.dateOnly(widget.selectedDate!) : null;
Expand All @@ -224,7 +233,8 @@ class _DatePickerState extends State<DatePicker> {
@override
void didUpdateWidget(covariant DatePicker oldWidget) {
if (oldWidget.initialDate != widget.initialDate) {
_displayedDate = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedDate = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);
}
if (oldWidget.initialPickerType != widget.initialPickerType) {
_pickerType = widget.initialPickerType;
Expand Down Expand Up @@ -307,8 +317,14 @@ class _DatePickerState extends State<DatePicker> {
});
},
onDateSelected: (selectedMonth) {
// clamped the initial date to fall between min and max date.
final clampedSelectedMonth = DateUtilsX.clampDateToRange(
min: widget.minDate,
max: widget.maxDate,
date: selectedMonth,
);
setState(() {
_displayedDate = selectedMonth;
_displayedDate = clampedSelectedMonth;
_pickerType = PickerType.days;
});
},
Expand Down Expand Up @@ -339,8 +355,14 @@ class _DatePickerState extends State<DatePicker> {
highlightColor: widget.highlightColor,
splashRadius: widget.splashRadius,
onDateSelected: (selectedYear) {
// clamped the initial date to fall between min and max date.
final clampedSelectedYear = DateUtilsX.clampDateToRange(
min: widget.minDate,
max: widget.maxDate,
date: selectedYear,
);
setState(() {
_displayedDate = selectedYear;
_displayedDate = clampedSelectedYear;
_pickerType = PickerType.months;
});
},
Expand Down
24 changes: 17 additions & 7 deletions lib/src/date/days_picker.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:date_picker_plus/src/shared/utils.dart';
import 'package:flutter/material.dart';

import 'days_view.dart';
Expand All @@ -22,9 +23,13 @@ import 'show_date_picker_dialog.dart';
class DaysPicker extends StatefulWidget {
/// Creates a days picker.
///
/// It will display a grid of days for the [initialDate]'s month. if that
/// is null, `DateTime.now()` will be used. The day
/// indicated by [selectedDate] will be selected if provided.
/// It will display a grid of days for the [initialDate]'s month. If [initialDate]
/// is null, `DateTime.now()` will be used. If `DateTime.now()` does not fall within
/// the valid range of [minDate] and [maxDate], it will fall back to the nearest
/// valid date from `DateTime.now()`, selecting the [maxDate] if `DateTime.now()` is
/// after the valid range, or [minDate] if before.
///
/// The day indicated by [selectedDate] will be selected if provided.
///
/// The optional [onDateSelected] callback will be called if provided when a date
/// is selected.
Expand Down Expand Up @@ -89,8 +94,11 @@ class DaysPicker extends StatefulWidget {
);
}

/// The date which will be displayed on first opening.
/// If not specified, the picker will default to `DateTime.now()` date.
/// The date which will be displayed on first opening. If not specified, the picker
/// will default to `DateTime.now()`. If `DateTime.now()` does not fall within the
/// valid range of [minDate] and [maxDate], it will automatically adjust to the nearest
/// valid date, selecting [maxDate] if `DateTime.now()` is after the valid range, or
/// [minDate] if it is before.
///
/// Note that only dates are considered. time fields are ignored.
final DateTime? initialDate;
Expand Down Expand Up @@ -230,7 +238,8 @@ class _DaysPickerState extends State<DaysPicker> {

@override
void initState() {
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);
_selectedDate = widget.selectedDate != null ? DateUtils.dateOnly(widget.selectedDate!) : null;
_pageController = PageController(
initialPage: DateUtils.monthDelta(widget.minDate, _displayedMonth!),
Expand All @@ -245,7 +254,8 @@ class _DaysPickerState extends State<DaysPicker> {
// but for makeing debuging easy, we will navigate to the initial date again
// if it changes.
if (oldWidget.initialDate != widget.initialDate) {
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);

_pageController.jumpToPage(
DateUtils.monthDelta(widget.minDate, _displayedMonth!),
Expand Down
14 changes: 10 additions & 4 deletions lib/src/range/range_days_picker.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:date_picker_plus/src/shared/utils.dart';
import 'package:flutter/material.dart';

import '../shared/header.dart';
Expand Down Expand Up @@ -60,8 +61,11 @@ class RangeDaysPicker extends StatefulWidget {
);
}

/// The date which will be displayed on first opening.
/// If not specified, the picker will default to `DateTime.now()` date.
/// The date which will be displayed on first opening. If not specified, the picker
/// will default to `DateTime.now()`. If `DateTime.now()` does not fall within the
/// valid range of [minDate] and [maxDate], it will automatically adjust to the nearest
/// valid date, selecting [maxDate] if `DateTime.now()` is after the valid range, or
/// [minDate] if it is before.
///
/// Note that only dates are considered. time fields are ignored.
final DateTime? initialDate;
Expand Down Expand Up @@ -213,7 +217,8 @@ class __RangeDaysPickerState extends State<RangeDaysPicker> {

@override
void initState() {
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);
_pageController = PageController(
initialPage: DateUtils.monthDelta(widget.minDate, _displayedMonth!),
);
Expand All @@ -228,7 +233,8 @@ class __RangeDaysPickerState extends State<RangeDaysPicker> {
// but for makeing debuging easy, we will navigate to the initial date again
// if it changes.
if (oldWidget.initialDate != widget.initialDate) {
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedMonth = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);
_pageController.jumpToPage(
DateUtils.monthDelta(widget.minDate, _displayedMonth!),
);
Expand Down
40 changes: 31 additions & 9 deletions lib/src/range/range_picker.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:date_picker_plus/src/shared/utils.dart';
import 'package:flutter/material.dart';

import '../shared/month_picker.dart';
Expand All @@ -23,9 +24,13 @@ import 'range_days_picker.dart';
class RangeDatePicker extends StatefulWidget {
/// Creates a calendar range picker.
///
/// It will display a grid of days for the [initialDate]'s month. if that
/// is null, `DateTime.now()` will be used. The day
/// indicated by [selectedRange] will be selected if provided.
/// It will display a grid of days for the [initialDate]'s month. If [initialDate]
/// is null, `DateTime.now()` will be used. If `DateTime.now()` does not fall within
/// the valid range of [minDate] and [maxDate], it will fall back to the nearest
/// valid date from `DateTime.now()`, selecting the [maxDate] if `DateTime.now()` is
/// after the valid range, or [minDate] if before.
///
/// The day indicated by [selectedRange] will be selected if provided.
///
/// The optional [onRangeSelected] callback will be called if provided
/// when a range is selected.
Expand Down Expand Up @@ -88,8 +93,11 @@ class RangeDatePicker extends StatefulWidget {
/// Note that only dates are considered. time fields are ignored.
final DateTime? currentDate;

/// The date to which the picker will be initially opened.
/// If not specified, the picker will default to today's date.
/// The date which will be displayed on first opening. If not specified, the picker
/// will default to `DateTime.now()`. If `DateTime.now()` does not fall within the
/// valid range of [minDate] and [maxDate], it will automatically adjust to the nearest
/// valid date, selecting [maxDate] if `DateTime.now()` is after the valid range, or
/// [minDate] if it is before.
///
/// Note that only dates are considered. time fields are ignored.
final DateTime? initialDate;
Expand Down Expand Up @@ -242,7 +250,8 @@ class _RangeDatePickerState extends State<RangeDatePicker> {
@override
void initState() {
_pickerType = widget.initialPickerType;
_diplayedDate = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_diplayedDate = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);

if (widget.selectedRange != null) {
_selectedStartDate = DateUtils.dateOnly(widget.selectedRange!.start);
Expand All @@ -269,7 +278,8 @@ class _RangeDatePickerState extends State<RangeDatePicker> {
}

if (widget.initialDate != oldWidget.initialDate) {
_diplayedDate = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_diplayedDate = DateUtils.dateOnly(widget.initialDate ?? clampedInitailDate);
}

super.didUpdateWidget(oldWidget);
Expand Down Expand Up @@ -364,8 +374,14 @@ class _RangeDatePickerState extends State<RangeDatePicker> {
});
},
onDateSelected: (selectedMonth) {
// clamped the initial date to fall between min and max date.
final clampedSelectedMonth = DateUtilsX.clampDateToRange(
min: widget.minDate,
max: widget.maxDate,
date: selectedMonth,
);
setState(() {
_diplayedDate = selectedMonth;
_diplayedDate = clampedSelectedMonth;
_pickerType = PickerType.days;
});
},
Expand Down Expand Up @@ -396,8 +412,14 @@ class _RangeDatePickerState extends State<RangeDatePicker> {
highlightColor: widget.highlightColor,
splashRadius: widget.splashRadius,
onDateSelected: (selectedYear) {
// clamped the initial date to fall between min and max date.
final clampedSelectedYear = DateUtilsX.clampDateToRange(
min: widget.minDate,
max: widget.maxDate,
date: selectedYear,
);
setState(() {
_diplayedDate = selectedYear;
_diplayedDate = clampedSelectedYear;
_pickerType = PickerType.months;
});
},
Expand Down
25 changes: 16 additions & 9 deletions lib/src/shared/month_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ import '../date/show_date_picker_dialog.dart';
class MonthPicker extends StatefulWidget {
/// Creates a month picker.
///
/// It will display a grid of months for the [initialDate]'s year. if that
/// is null, `DateTime.now()` will be used. The month
/// indicated by [selectedDate] will be selected if provided.
/// It will display a grid of months for the [initialDate]'s year. If [initialDate]
/// is null, `DateTime.now()` will be used. If `DateTime.now()` does not fall within
/// the valid range of [minDate] and [maxDate], it will fall back to the nearest
/// valid date from `DateTime.now()`, selecting the [maxDate] if `DateTime.now()` is
/// after the valid range, or [minDate] if before.
///
/// The month indicated by [selectedDate] will be selected if provided.
///
/// The optional [onDateSelected] callback will be called if provided when a date
/// is selected.
Expand Down Expand Up @@ -90,8 +94,11 @@ class MonthPicker extends StatefulWidget {
);
}

/// The date which will be displayed on first opening.
/// If not specified, the picker will default to `DateTime.now()` date.
/// The date which will be displayed on first opening. If not specified, the picker
/// will default to `DateTime.now()`. If `DateTime.now()` does not fall within the
/// valid range of [minDate] and [maxDate], it will automatically adjust to the nearest
/// valid date, selecting [maxDate] if `DateTime.now()` is after the valid range, or
/// [minDate] if it is before.
///
/// Note that only year & month are considered. time & day fields are ignored.
final DateTime? initialDate;
Expand Down Expand Up @@ -223,7 +230,8 @@ class _MonthPickerState extends State<MonthPicker> {

@override
void initState() {
_displayedYear = DateUtilsX.yearOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedYear = DateUtilsX.yearOnly(widget.initialDate ?? clampedInitailDate);

_selectedDate = widget.selectedDate != null ? DateUtilsX.monthOnly(widget.selectedDate!) : null;
_pageController = PageController(
Expand All @@ -239,13 +247,12 @@ class _MonthPickerState extends State<MonthPicker> {
// but for makeing debuging easy, we will navigate to the initial date again
// if it changes.
if (oldWidget.initialDate != widget.initialDate) {
// _displayedYear = DateUtils.dateOnly(widget.initialDate ?? DateTime.now());
_displayedYear = DateUtilsX.yearOnly(widget.initialDate ?? DateTime.now());
final clampedInitailDate = DateUtilsX.clampDateToRange(max: widget.maxDate, min: widget.minDate, date: DateTime.now());
_displayedYear = DateUtilsX.yearOnly(widget.initialDate ?? clampedInitailDate);
_pageController.jumpToPage(_displayedYear!.year - widget.minDate.year);
}

if (oldWidget.selectedDate != _selectedDate) {
// _selectedDate = widget.selectedDate != null ? DateUtils.dateOnly(widget.selectedDate!) : null;
_selectedDate = widget.selectedDate != null ? DateUtilsX.monthOnly(widget.selectedDate!) : null;
}
super.didUpdateWidget(oldWidget);
Expand Down
18 changes: 18 additions & 0 deletions lib/src/shared/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,22 @@ extension DateUtilsX on DateUtils {
static DateTime yearOnly(DateTime date) {
return DateTime(date.year);
}

/// Clamps a [date] to fall within the provided [min] and [max] range.
///
/// If [date] is before [min], this method returns [min]. If [date] is after [max],
/// it returns [max]. Otherwise, it returns the [date] unchanged.
///
/// This is useful for ensuring a DateTime value stays within a specific range.
///
/// Returns the clamped [DateTime].
static DateTime clampDateToRange({
required DateTime min,
required DateTime max,
required DateTime date,
}) {
if (date.isBefore(min)) return min;
if (date.isAfter(max)) return max;
return date;
}
}
Loading

0 comments on commit 129fd41

Please sign in to comment.