Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ class ExampleApp extends StatelessWidget {
),
),
),
SizedBox(
width: buttonWidth,
child: ElevatedButton(
onPressed: () {
_openDateTimePicker(
context,
);
},
child: Text('Date time Picker', textAlign: TextAlign.center),
),
),
SizedBox(
width: buttonWidth,
child: ElevatedButton(
Expand Down Expand Up @@ -599,6 +610,9 @@ class ExampleApp extends StatelessWidget {
BuildContext context,
) {
BottomPicker.dateTime(
hourPredicate: (hour) {
return hour > 6;
},
headerBuilder: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Expand Down
5 changes: 5 additions & 0 deletions lib/bottom_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class BottomPicker extends StatefulWidget {
this.headerBuilder,
this.calendarDays = CupertinoDatePickerWidget.fullWeek,
this.diameterRatio = 1.1,
this.hourPredicate,
}) {
datePickerMode = CupertinoDatePickerMode.dateAndTime;
bottomPickerType = BottomPickerType.dateTime;
Expand Down Expand Up @@ -811,6 +812,9 @@ class BottomPicker extends StatefulWidget {
/// The bottom picker selector diameter ratio.
final double diameterRatio;

/// A predicate that can be used to select which hours are selectable.
SelectableHourPredicate? hourPredicate;

///display the bottom picker popup
///[context] the app context to display the popup
void show(BuildContext context) {
Expand Down Expand Up @@ -1023,6 +1027,7 @@ class BottomPickerState extends State<BottomPicker> {
itemExtent: widget.itemExtent,
showTimeSeparator: widget.showTimeSeparator,
pickerThemeData: widget.pickerThemeData,
hourPredicate: widget.hourPredicate,
)
: widget.bottomPickerType ==
BottomPickerType.year
Expand Down
43 changes: 40 additions & 3 deletions lib/cupertino/cupertino_date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
library;

import 'dart:math' as math;

import 'package:flutter/cupertino.dart';
import 'package:flutter/scheduler.dart';

Expand Down Expand Up @@ -47,6 +46,9 @@ const double _kTimerPickerLabelFontSize = 17.0;
// The width of each column of the countdown time picker.
const double _kTimerPickerColumnIntrinsicWidth = 106;

/// Signature for predicating hour for enabled hours selections.
typedef SelectableHourPredicate = bool Function(int hour);

TextStyle _themeTextStyle(BuildContext context, {bool isValid = true}) {
final TextStyle style =
CupertinoTheme.of(context).textTheme.dateTimePickerTextStyle;
Expand Down Expand Up @@ -284,6 +286,7 @@ class CupertinoDatePickerWidget extends StatefulWidget {
this.selectionOverlayBuilder,
this.showTimeSeparator = false,
this.calendarDays = fullWeek,
this.selectableHourPredicate,
}) : initialDateTime = initialDateTime ?? DateTime.now(),
assert(
itemExtent > 0,
Expand Down Expand Up @@ -336,6 +339,11 @@ class CupertinoDatePickerWidget extends StatefulWidget {
this.initialDateTime.minute % minuteInterval == 0,
'initial minute is not divisible by minute interval',
);
assert(
(selectableHourPredicate == null) ||
(selectableHourPredicate?.call(this.initialDateTime.hour) == true),
'Hour must satisfy the hour predicate',
);
}

/// The mode of the date picker as one of [CupertinoDatePickerMode]. Defaults
Expand Down Expand Up @@ -468,6 +476,9 @@ class CupertinoDatePickerWidget extends StatefulWidget {
/// {@end-tool}
final SelectionOverlayBuilder? selectionOverlayBuilder;

//TODO add docs
final SelectableHourPredicate? selectableHourPredicate;

static const List<int> fullWeek = <int>[
DateTime.monday,
DateTime.tuesday,
Expand Down Expand Up @@ -853,6 +864,8 @@ class _CupertinoDatePickerDateTimeState

if (isDateInvalid) {
return;
} else if (widget.selectableHourPredicate?.call(selected.hour) == false) {
return;
}

widget.onDateTimeChanged(selected);
Expand Down Expand Up @@ -939,18 +952,23 @@ class _CupertinoDatePickerDateTimeState
// `hourIndex`, is it possible to change the value of the minute picker, so
// that the resulting date stays in the valid range.
bool _isValidHour(int meridiemIndex, int hourIndex) {
int selectedHour = _selectedHour(meridiemIndex, hourIndex);
final DateTime rangeStart = DateTime(
initialDateTime.year,
initialDateTime.month,
initialDateTime.day + selectedDayFromInitial,
_selectedHour(meridiemIndex, hourIndex),
selectedHour,
);

// The end value of the range is exclusive, i.e. [rangeStart, rangeEnd).
final DateTime rangeEnd = rangeStart.add(const Duration(hours: 1));

bool hourInIntevalPredicate =
widget.selectableHourPredicate?.call(selectedHour) ?? true;

return (widget.minimumDate?.isBefore(rangeEnd) ?? true) &&
!(widget.maximumDate?.isBefore(rangeStart) ?? false);
!(widget.maximumDate?.isBefore(rangeStart) ?? false) &&
hourInIntevalPredicate;
}

Widget _buildHourPicker(
Expand Down Expand Up @@ -1176,6 +1194,24 @@ class _CupertinoDatePickerDateTimeState
}
}

void _checkOnHourDisplay() {
final DateTime selectedDate = selectedDateTime;
final bool minCheck = widget.minimumDate?.isAfter(selectedDate) ?? false;

if (widget.selectableHourPredicate?.call(selectedDate.hour) == false) {
const int daysThreshold = 1;
final DateTime targetDate =
selectedDate.add(const Duration(hours: daysThreshold));

_scrollToDate(
targetDate,
selectedDate,
minCheck,
newItemIndex: dateController.selectedItem + daysThreshold,
);
}
}

// One or more pickers have just stopped scrolling.
void _pickerDidStopScrolling() {
// Call setState to update the greyed out date/hour/minute/meridiem.
Expand All @@ -1193,6 +1229,7 @@ class _CupertinoDatePickerDateTimeState
final bool maxCheck = widget.maximumDate?.isBefore(selectedDate) ?? false;

_checkOnCustomDaysDisplay();
_checkOnHourDisplay();
if (minCheck || maxCheck) {
// We have minCheck === !maxCheck.
final DateTime targetDate =
Expand Down
3 changes: 3 additions & 0 deletions lib/widgets/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class DatePicker extends StatelessWidget {
final bool showTimeSeparator;
final List<int> calendarDays;
final CupertinoTextThemeData? pickerThemeData;
final SelectableHourPredicate? hourPredicate;

const DatePicker({
super.key,
Expand All @@ -31,6 +32,7 @@ class DatePicker extends StatelessWidget {
this.showTimeSeparator = false,
this.calendarDays = CupertinoDatePickerWidget.fullWeek,
this.pickerThemeData,
this.hourPredicate,
});

@override
Expand All @@ -46,6 +48,7 @@ class DatePicker extends StatelessWidget {
itemExtent: itemExtent ?? 0,
showTimeSeparator: showTimeSeparator,
mode: mode,
selectableHourPredicate: hourPredicate,
onDateTimeChanged: onDateChanged,
initialDateTime: initialDateTime,
minuteInterval: minuteInterval,
Expand Down