diff --git a/example/lib/main.dart b/example/lib/main.dart index a3d6e83..943aafd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -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(), + ); } } diff --git a/lib/src/date_time_picker.dart b/lib/src/date_time_picker.dart index 0ddc31f..6af15da 100644 --- a/lib/src/date_time_picker.dart +++ b/lib/src/date_time_picker.dart @@ -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 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? markedDates; + + /// a [List] of [DateTime] objects that will be disabled and cannot be interacted with whatsoever. final List? disabledDates; + + /// a [List] of [TimeOfDay] objects that cannot be picked in the [TimePickerDialog]. final List? disabledTimes; @override @@ -265,7 +253,7 @@ class _DateTimePickerState extends State { } } -bool useTimeFormatBasedOnLocale() { +bool _useTimeFormatBasedOnLocale() { // Get LocaleName of current platform and split language- and countryCode in 2 List values. List deviceLocale = Platform.localeName.split('_'); diff --git a/lib/src/enums/date_box_shape.dart b/lib/src/enums/date_box_shape.dart index 6b628db..9010f11 100644 --- a/lib/src/enums/date_box_shape.dart +++ b/lib/src/enums/date_box_shape.dart @@ -1,4 +1,5 @@ +/// Defines the shape of a specific date. enum DateBoxShape { circle, rectangle, diff --git a/lib/src/extensions/date_time.dart b/lib/src/extensions/date_time.dart index a3e3245..6c82034 100644 --- a/lib/src/extensions/date_time.dart +++ b/lib/src/extensions/date_time.dart @@ -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 dates) { - return dates.any((element) => element.sameDayAs(this)); + /// Check if the current [DateTime] contains any the given [dates] + bool containsAny(List dates) { + return dates.any((element) => element.equals(this)); } + // Return a [List] of [DateTime] objects of the week the current [DateTime] is in. List 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; diff --git a/lib/src/extensions/time_of_day.dart b/lib/src/extensions/time_of_day.dart index 6be4997..0e0faf5 100644 --- a/lib/src/extensions/time_of_day.dart +++ b/lib/src/extensions/time_of_day.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; extension TimeOfDayExtension on TimeOfDay { - bool timeContainedIn(List times) { - return times.any((element) => element.sameTimeAs(this)); + /// Check if the current [TimeOfDay] contains any the given [times] + bool containsAny(List 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; } } diff --git a/lib/src/utils/date_time_picker_controller.dart b/lib/src/utils/date_time_picker_controller.dart index 099379e..ae44af8 100644 --- a/lib/src/utils/date_time_picker_controller.dart +++ b/lib/src/utils/date_time_picker_controller.dart @@ -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? markedDates; diff --git a/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker.dart b/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker.dart index d02f661..09a7c00 100644 --- a/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker.dart +++ b/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker.dart @@ -82,31 +82,34 @@ class MonthDateTimePicker extends StatelessWidget { context, dateTimePickerController); } - if (timeOfDay != null && - timeOfDay.timeContainedIn( - dateTimePickerController.disabledTimes ?? [])) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Verkeerde tijd gekozen'), - content: SingleChildScrollView( - child: ListBody( - children: const [ - Text( - 'De tijd die u wilt kiezen, is niet mogelijk, maak een andere keuze.'), - ], - ), - ), - actions: [ - TextButton( - child: const Text('OK'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ), - ); + if (dateTimePickerController.wrongTimeDialog != null) { + if (timeOfDay != null && + timeOfDay.containsAny( + dateTimePickerController.disabledTimes ?? [], + )) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Verkeerde tijd gekozen'), + content: SingleChildScrollView( + child: ListBody( + children: const [ + Text( + 'De tijd die u wilt kiezen, is niet mogelijk, maak een andere keuze.'), + ], + ), + ), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); + } } DateTime selectedDateTime = DateTime( @@ -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 ?? [], ); } diff --git a/lib/src/widgets/week_date_time_picker/week_date_time_picker.dart b/lib/src/widgets/week_date_time_picker/week_date_time_picker.dart index 88fe860..0670cb1 100644 --- a/lib/src/widgets/week_date_time_picker/week_date_time_picker.dart +++ b/lib/src/widgets/week_date_time_picker/week_date_time_picker.dart @@ -48,49 +48,48 @@ 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 (timeOfDay != null && - timeOfDay.timeContainedIn( - dateTimePickerController.disabledTimes ?? [])) { - showDialog( + if (dateTimePickerController.wrongTimeDialog != null) { + if (timeOfDay != null && + timeOfDay.containsAny( + dateTimePickerController.disabledTimes ?? [], + )) { + showDialog( context: context, - builder: (context) { - return AlertDialog( - title: const Text('Verkeerde tijd gekozen'), - content: SingleChildScrollView( - child: ListBody( - children: const [ - Text( - 'De tijd die u wilt kiezen, is niet mogelijk, maak een andere keuze.'), - ], - ), + builder: (context) => AlertDialog( + title: const Text('Verkeerde tijd gekozen'), + content: SingleChildScrollView( + child: ListBody( + children: const [ + Text( + 'De tijd die u wilt kiezen, is niet mogelijk, maak een andere keuze.'), + ], ), - actions: [ - TextButton( - child: const Text('OK'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ); - }); - } else { - timeOfDay = const TimeOfDay( - hour: 0, - minute: 0, - ); + ), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ); + } } 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( diff --git a/test/date_time_extension_test.dart b/test/date_time_extension_test.dart index 5b2facd..805d3b4 100644 --- a/test/date_time_extension_test.dart +++ b/test/date_time_extension_test.dart @@ -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) diff --git a/test/time_of_day_extension_test.dart b/test/time_of_day_extension_test.dart index bd20043..4e2b1f8 100644 --- a/test/time_of_day_extension_test.dart +++ b/test/time_of_day_extension_test.dart @@ -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)