Updated documentation, used better naming convention extension methods, refactored error dialog when choosing a disabled time

This commit is contained in:
Thomas Klein Langenhorst 2022-09-02 17:04:47 +02:00
parent 6b3f1f5d12
commit fc42f21b1b
10 changed files with 133 additions and 133 deletions

View file

@ -28,9 +28,10 @@ class MyApp extends StatelessWidget {
class DatePickerDemo extends StatelessWidget {
const DatePickerDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return DateTimePicker(dateTimePickerTheme: const DateTimePickerTheme(),);
return DateTimePicker(
dateTimePickerTheme: const DateTimePickerTheme(),
);
}
}

View file

@ -8,52 +8,22 @@ import 'package:flutter_date_time_picker/src/widgets/week_date_time_picker/week_
import 'package:intl/date_symbol_data_local.dart';
/// A widget that displays a date picker from a sheet form the top of the screen.
/// This sheet displays initialy displays a week of days but can be dragged down to show full months.
/// This sheet displays initially displays a week but can be dragged down to show a full month.
/// Both views can be dragged sideways to show the next or previous week/month.
///
/// The child will be the [Widget] that is displayed underneath the date picker in the stack.
///
///
/// [initialDate] indicates the starting date. Default is [DateTime.now()
///
/// [pickTime] is a [bool] that determines if the user is able to pick a time after picking a date.
/// true will always tirgger a [TimePickerDialog].
/// false will nvever trigger a [TimePickerDialog]. This is default.
///
/// [use24HourFormat] is a [bool] to set de clock on [TimePickerDialog] to a fixed 24 or 12-hour format.
/// By default this gets determined by the [Locale] on the device.
///
/// [dateTimePickerTheme] is used the set the global theme of the [DateTimePicker]
///
/// The header [Widget] will be displayed above the date picker in the modal sheet in a column.
///
/// [onTapDay] is a callback that provides the taped date as a [DateTime] object.
///
/// [highlightToday] is a [bool] that determines which day shall we highlighted.
/// true will always highlight the current date. This is standard.
/// false will highlight the currently selected date by either the initial date or the one chosen by the user.
/// final Widget? child;
///
/// [markedDates] contain the dates [DateTime] that will be marked in the picker by a small dot.
///
/// [disabledDates] contain the dates [DateTime] that will be disabled and cannot be interacted with whatsoever.
///
/// [disabledTimes] contain the time [TimeOfDay] that cannot be picked in the [TimePickerDialog].
///
///
/// Example:
/// ```dart
/// DatePicker(
/// dateTimePickerTheme: const DateTimePickerTheme()
/// initialDate: selectedDate,
/// highlightToday: false,
/// highlightToday: true,
/// onTapDay: (date) {
/// setState(() {
/// selectedDate = date;
/// });
/// },
/// markedDates: [
/// DateTime(2022, 7, 22),
/// DateTime(2022, 3, 14),
/// ],
/// header: Container(
/// height: 100,
@ -68,7 +38,7 @@ import 'package:intl/date_symbol_data_local.dart';
/// height: 34,
/// child: Center(
/// child: Text(
/// 'Persoonlijk',
/// 'Personal calendar',
/// style: TextStyle(
/// fontSize: 16,
/// fontWeight: FontWeight.w900,
@ -97,7 +67,7 @@ import 'package:intl/date_symbol_data_local.dart';
/// ),
/// child: const Center(
/// child: Text(
/// 'Teamplanning',
/// 'Work calendar',
/// style: TextStyle(
/// color: Colors.white,
/// fontSize: 16,
@ -113,15 +83,7 @@ import 'package:intl/date_symbol_data_local.dart';
/// margin: const EdgeInsets.only(
/// top: 195,
/// ),
/// child: ShellRoster(
/// startHour: 0,
/// endHour: 24,
/// blocks: [
/// for (Map<String, TimeOfDay> block in blocks) ...[
/// getBlock(block),
/// ],
/// ],
/// ),
/// child: HolidayRoster(),
/// ),
/// ),
///```
@ -131,6 +93,7 @@ class DateTimePicker extends StatefulWidget {
this.header,
this.onTapDay,
this.highlightToday = true,
this.wrongTimeDialog,
bool? use24HourFormat,
this.pickTime = false,
this.initialDate,
@ -140,19 +103,44 @@ class DateTimePicker extends StatefulWidget {
this.child,
super.key,
}) {
alwaysUse24HourFormat = use24HourFormat ?? useTimeFormatBasedOnLocale();
alwaysUse24HourFormat = use24HourFormat ?? _useTimeFormatBasedOnLocale();
}
/// The child contained by the DatePicker.
final Widget? child;
/// A [Widget] to display when the user picks a disabled time in the [TimePickerDialog]
final Widget? wrongTimeDialog;
/// Visual properties for the [DateTimePicker]
final DateTimePickerTheme dateTimePickerTheme;
/// Widget shown at the top of the [DateTimePicker]
final Widget? header;
/// Callback that provides the date tapped on as a [DateTime] object.
final Function(DateTime)? onTapDay;
/// Whether the current day should be highlighted in the [DateTimePicker]
final bool highlightToday;
/// a [bool] to set de clock on [TimePickerDialog] to a fixed 24 or 12-hour format.
/// By default this gets determined by the [Locale] on the device.
late final bool alwaysUse24HourFormat;
/// [pickTime] is a [bool] that determines if the user is able to pick a time after picking a date usring the [TimePickerDialog].
final bool pickTime;
/// indicates the starting date. Default is [DateTime.now()]
final DateTime? initialDate;
/// [markedDates] contain the dates [DateTime] that will be marked in the [DateTimePicker] by a small dot.
final List<DateTime>? markedDates;
/// a [List] of [DateTime] objects that will be disabled and cannot be interacted with whatsoever.
final List<DateTime>? disabledDates;
/// a [List] of [TimeOfDay] objects that cannot be picked in the [TimePickerDialog].
final List<TimeOfDay>? disabledTimes;
@override
@ -265,7 +253,7 @@ class _DateTimePickerState extends State<DateTimePicker> {
}
}
bool useTimeFormatBasedOnLocale() {
bool _useTimeFormatBasedOnLocale() {
// Get LocaleName of current platform and split language- and countryCode in 2 List values.
List<String> deviceLocale = Platform.localeName.split('_');

View file

@ -1,4 +1,5 @@
/// Defines the shape of a specific date.
enum DateBoxShape {
circle,
rectangle,

View file

@ -1,16 +1,17 @@
extension DateTimeExtension on DateTime {
// Check if the current date is the same as the given date
bool sameDayAs(DateTime selectedDate) {
/// Check if the current [DateTime] is the same as the given [selectedDate]
bool equals(DateTime selectedDate) {
return selectedDate.day == day &&
selectedDate.month == month &&
selectedDate.year == year;
}
// Check if the current date is contained in the given list
bool dateContainedIn(List<DateTime> dates) {
return dates.any((element) => element.sameDayAs(this));
/// Check if the current [DateTime] contains any the given [dates]
bool containsAny(List<DateTime> dates) {
return dates.any((element) => element.equals(this));
}
// Return a [List] of [DateTime] objects of the week the current [DateTime] is in.
List<DateTime> daysOfWeek() {
var startFrom = subtract(Duration(days: weekday));
return List.generate(
@ -22,9 +23,11 @@ extension DateTimeExtension on DateTime {
);
}
/// Determine if a certain [year] of a [DateTime] object is a leap year.
bool get isLeapYear =>
(year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
/// Returns the amount of days in the current month of the [DateTime] object
int daysInMonth() {
late int amountOfDays;

View file

@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
extension TimeOfDayExtension on TimeOfDay {
bool timeContainedIn(List<TimeOfDay> times) {
return times.any((element) => element.sameTimeAs(this));
/// Check if the current [TimeOfDay] contains any the given [times]
bool containsAny(List<TimeOfDay> times) {
return times.any((element) => element.equals(this));
}
bool sameTimeAs(TimeOfDay selectedTime) {
/// Check if the current [TimeOfDay] is the same as the given [selectedTime]
bool equals(TimeOfDay selectedTime) {
return selectedTime.hour == hour && selectedTime.minute == minute;
}
}

View file

@ -10,6 +10,7 @@ class DateTimePickerController extends ChangeNotifier {
required this.browsingDate,
required this.selectedDate,
this.header,
this.wrongTimeDialog,
this.markedDates,
this.disabledDates,
this.disabledTimes,
@ -23,6 +24,8 @@ class DateTimePickerController extends ChangeNotifier {
final Widget? header;
final Widget? wrongTimeDialog;
final DateTimePickerTheme theme;
final List<DateTime>? markedDates;

View file

@ -82,9 +82,11 @@ class MonthDateTimePicker extends StatelessWidget {
context, dateTimePickerController);
}
if (dateTimePickerController.wrongTimeDialog != null) {
if (timeOfDay != null &&
timeOfDay.timeContainedIn(
dateTimePickerController.disabledTimes ?? [])) {
timeOfDay.containsAny(
dateTimePickerController.disabledTimes ?? [],
)) {
showDialog(
context: context,
builder: (context) => AlertDialog(
@ -108,6 +110,7 @@ class MonthDateTimePicker extends StatelessWidget {
),
);
}
}
DateTime selectedDateTime = DateTime(
selectedDate.year,
@ -170,7 +173,7 @@ class MonthDateTimePicker extends StatelessWidget {
date.year,
date.month,
index + 1 - daysToSkip,
).sameDayAs(
).equals(
dateTimePickerController.highlightToday
? DateTime.now()
: dateTimePickerController.selectedDate,
@ -217,7 +220,7 @@ class MonthDateTimePicker extends StatelessWidget {
date.year,
date.month,
index + 1 - daysToSkip,
).dateContainedIn(
).containsAny(
dateTimePickerController.disabledDates ?? [],
);
}
@ -227,7 +230,7 @@ class MonthDateTimePicker extends StatelessWidget {
date.year,
date.month,
index + 1 - daysToSkip,
).sameDayAs(dateTimePickerController.selectedDate);
).equals(dateTimePickerController.selectedDate);
}
bool shouldMark(int index, int daysToSkip) {
@ -235,7 +238,7 @@ class MonthDateTimePicker extends StatelessWidget {
date.year,
date.month,
index + 1 - daysToSkip,
).sameDayAs(
).equals(
dateTimePickerController.highlightToday
? DateTime.now()
: dateTimePickerController.selectedDate,
@ -244,7 +247,7 @@ class MonthDateTimePicker extends StatelessWidget {
date.year,
date.month,
index + 1 - daysToSkip,
).dateContainedIn(
).containsAny(
dateTimePickerController.markedDates ?? [],
);
}

View file

@ -48,18 +48,21 @@ class WeekDateTimePicker extends StatelessWidget {
DateTime selectedDate = date.daysOfWeek()[index];
timeOfDay = const TimeOfDay(hour: 0, minute: 0);
if (dateTimePickerController.pickTime) {
timeOfDay = await displayTimePicker(
context, dateTimePickerController);
}
if (dateTimePickerController.wrongTimeDialog != null) {
if (timeOfDay != null &&
timeOfDay.timeContainedIn(
dateTimePickerController.disabledTimes ?? [])) {
timeOfDay.containsAny(
dateTimePickerController.disabledTimes ?? [],
)) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
builder: (context) => AlertDialog(
title: const Text('Verkeerde tijd gekozen'),
content: SingleChildScrollView(
child: ListBody(
@ -77,20 +80,16 @@ class WeekDateTimePicker extends StatelessWidget {
},
),
],
),
);
});
} else {
timeOfDay = const TimeOfDay(
hour: 0,
minute: 0,
);
}
}
DateTime selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
timeOfDay.hour,
timeOfDay!.hour,
timeOfDay.minute,
);
@ -191,7 +190,7 @@ class WeekDateTimePicker extends StatelessWidget {
}
bool shouldHighlight(int index) {
return date.daysOfWeek().elementAt(index).sameDayAs(
return date.daysOfWeek().elementAt(index).equals(
dateTimePickerController.highlightToday
? DateTime.now()
: dateTimePickerController.selectedDate,
@ -202,18 +201,18 @@ class WeekDateTimePicker extends StatelessWidget {
return date
.daysOfWeek()
.elementAt(index)
.sameDayAs(dateTimePickerController.selectedDate);
.equals(dateTimePickerController.selectedDate);
}
bool isDisabled(int index) {
return date
.daysOfWeek()
.elementAt(index)
.dateContainedIn(dateTimePickerController.disabledDates ?? []);
.containsAny(dateTimePickerController.disabledDates ?? []);
}
bool shouldMark(int index) {
return !date.daysOfWeek().elementAt(index).sameDayAs(
return !date.daysOfWeek().elementAt(index).equals(
dateTimePickerController.highlightToday
? DateTime.now()
: dateTimePickerController.selectedDate,
@ -221,7 +220,7 @@ class WeekDateTimePicker extends StatelessWidget {
date
.daysOfWeek()
.elementAt(index)
.dateContainedIn(dateTimePickerController.markedDates ?? []);
.containsAny(dateTimePickerController.markedDates ?? []);
}
BorderRadius _determineBorderRadius(

View file

@ -3,16 +3,16 @@ import 'package:flutter_date_time_picker/src/extensions/date_time.dart';
void main() {
group('DateTimeExtension', () {
test('sameDayAs() should return true if the same date, if not false', () {
expect(DateTime(2022, 01, 01).sameDayAs(DateTime(2022, 01, 01)), true);
expect(DateTime(2022, 01, 01).sameDayAs(DateTime(2022, 01, 02)), false);
test('equals() should return true if the same date, if not false', () {
expect(DateTime(2022, 01, 01).equals(DateTime(2022, 01, 01)), true);
expect(DateTime(2022, 01, 01).equals(DateTime(2022, 01, 02)), false);
});
test(
'dateContainedIn() should return a boolean if the date is found in a list of dates or not',
'containsAny() should return a boolean if the date is found in a list of dates or not',
() {
expect(
DateTime(2022, 01, 01).dateContainedIn([
DateTime(2022, 01, 01).containsAny([
DateTime(2022, 01, 01),
DateTime(2022, 01, 02),
DateTime(2022, 01, 03)
@ -20,7 +20,7 @@ void main() {
true);
expect(
DateTime(2022, 01, 01).dateContainedIn([
DateTime(2022, 01, 01).containsAny([
DateTime(2022, 01, 02),
DateTime(2022, 01, 03),
DateTime(2022, 01, 04)

View file

@ -4,22 +4,22 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
group('TimeOfDayExtension', () {
test('sameTimeAs() should return true if the same time, if not false', () {
test('equals() should return true if the same time, if not false', () {
expect(
const TimeOfDay(hour: 12, minute: 0)
.sameTimeAs(const TimeOfDay(hour: 12, minute: 0)),
.equals(const TimeOfDay(hour: 12, minute: 0)),
true);
expect(
const TimeOfDay(hour: 13, minute: 0)
.sameTimeAs(const TimeOfDay(hour: 12, minute: 0)),
.equals(const TimeOfDay(hour: 12, minute: 0)),
false);
});
test(
'timeContainedIn() should return a boolean if the time is found in a list of times or not',
'containsAny() should return a boolean if the time is found in a list of times or not',
() {
expect(
const TimeOfDay(hour: 12, minute: 0).timeContainedIn(const [
const TimeOfDay(hour: 12, minute: 0).containsAny(const [
TimeOfDay(hour: 10, minute: 0),
TimeOfDay(hour: 11, minute: 0),
TimeOfDay(hour: 12, minute: 0)
@ -27,7 +27,7 @@ void main() {
true);
expect(
const TimeOfDay(hour: 12, minute: 0).timeContainedIn(const [
const TimeOfDay(hour: 12, minute: 0).containsAny(const [
TimeOfDay(hour: 9, minute: 0),
TimeOfDay(hour: 10, minute: 0),
TimeOfDay(hour: 11, minute: 0)