diff --git a/example/.metadata b/example/.metadata index 39f2501..31b763f 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 channel: stable project_type: app @@ -13,26 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 - platform: android - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 - platform: ios - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 - platform: linux - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 - platform: macos - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 - platform: web - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 - platform: windows - create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 - base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 # User provided section diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 0833ecf..2fb4ea1 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -44,7 +44,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" + applicationId "com.iconica.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion flutter.minSdkVersion diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index 45d523a..39e01cb 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.iconica.example"> + diff --git a/lib/flutter_date_time_picker.dart b/lib/flutter_date_time_picker.dart index b54352f..1f53b98 100644 --- a/lib/flutter_date_time_picker.dart +++ b/lib/flutter_date_time_picker.dart @@ -1,3 +1,9 @@ library flutter_date_time_picker; -export 'src/date_time_picker.dart'; +export 'src/date_time_picker.dart' show DateTimePicker; +export 'src/enums/date_box_shape.dart'; +export 'src/models/date_box_base_theme.dart'; +export 'src/models/date_box_disabled_theme.dart'; +export 'src/models/date_box_highlight_theme.dart'; +export 'src/models/date_box_selected_theme.dart'; +export 'src/models/date_time_picker_theme.dart'; \ No newline at end of file diff --git a/lib/src/date_time_picker.dart b/lib/src/date_time_picker.dart index 3b22eee..9e6c4ec 100644 --- a/lib/src/date_time_picker.dart +++ b/lib/src/date_time_picker.dart @@ -1,130 +1,168 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/src/models/date_time_picker_theme.dart'; import 'package:flutter_date_time_picker/src/utils/date_time_picker_controller.dart'; import 'package:flutter_date_time_picker/src/widgets/month_date_time_picker.dart/month_date_time_picker_sheet.dart'; import 'package:flutter_date_time_picker/src/widgets/week_date_time_picker/week_date_time_picker_sheet.dart'; 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. -/// Both views can be dragged sideways to show the next or previous wweek/month. -/// -/// The child will be the [Widget] that is displayed underneath the date picker in the stack. -/// -/// 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. -/// [highlighColor] is used as background for the highlighted day. -/// [toggleableActiveColor] is used for the text color when highlighted. -/// [disabledColor] is used for the text color when not highlighted. -/// -/// markedDates contain the dates [DateTime] that will be marked in the picker by a small dot. -/// [indicatorColor] is used for the color of the dot. -/// -/// Example: -/// ```dart -/// ShellDatePicker( -/// initialDate: selectedDate, -/// highlightToday: false, -/// onTapDay: (date) { -/// setState(() { -/// selectedDate = date; -/// }); -/// }, -/// markedDates: [ -/// DateTime(2022, 7, 22), -/// ], -/// header: Container( -/// height: 100, -/// width: MediaQuery.of(context).size.width, -/// padding: const EdgeInsets.only(bottom: 10), -/// child: Row( -/// crossAxisAlignment: CrossAxisAlignment.end, -/// mainAxisAlignment: MainAxisAlignment.center, -/// children: [ -/// const SizedBox( -/// width: 160, -/// height: 34, -/// child: Center( -/// child: Text( -/// 'Persoonlijk', -/// style: TextStyle( -/// fontSize: 16, -/// fontWeight: FontWeight.w900, -/// ), -/// ), -/// ), -/// ), -/// const SizedBox( -/// width: 4, -/// ), -/// Container( -/// width: 160, -/// height: 34, -/// decoration: BoxDecoration( -/// color: const Color(0xFF00273D), -/// borderRadius: const BorderRadius.all( -/// Radius.circular(10), -/// ), -/// boxShadow: [ -/// BoxShadow( -/// color: const Color(0xFF000000).withOpacity(0.50), -/// offset: const Offset(0, 6), -/// blurRadius: 9, -/// ), -/// ], -/// ), -/// child: const Center( -/// child: Text( -/// 'Teamplanning', -/// style: TextStyle( -/// color: Colors.white, -/// fontSize: 16, -/// fontWeight: FontWeight.w900, -/// ), -/// ), -/// ), -/// ), -/// ], -/// ), -/// ), -/// child: Container( -/// margin: const EdgeInsets.only( -/// top: 195, -/// ), -/// child: ShellRoster( -/// startHour: 0, -/// endHour: 24, -/// blocks: [ -/// for (Map block in blocks) ...[ -/// getBlock(block), -/// ], -/// ], -/// ), -/// ), -/// ), -///``` class DateTimePicker extends StatefulWidget { - const DateTimePicker({ - required this.child, + /// A widget that displays a date picker from a sheet form the top of the screen. + /// 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. + /// + /// Example: + /// ```dart + /// DatePicker( + /// dateTimePickerTheme: const DateTimePickerTheme() + /// initialDate: selectedDate, + /// highlightToday: true, + /// onTapDay: (date) { + /// setState(() { + /// selectedDate = date; + /// }); + /// }, + /// markedDates: [ + /// DateTime(2022, 3, 14), + /// ], + /// wrongTimeDialog: + /// AlertDialog( + /// title: const Text('Invalid Time'), + /// content: SingleChildScrollView( + /// child: ListBody( + /// children: const [ + /// Text( + /// 'The time you try to choose is diabled, try to pick another time.'), + /// ], + /// ), + /// ), + /// actions: [ + /// TextButton( + /// child: const Text('OK'), + /// onPressed: () { + /// Navigator.pop(context); + /// }, + /// ), + /// ], + /// ), + /// header: Container( + /// height: 100, + /// width: MediaQuery.of(context).size.width, + /// padding: const EdgeInsets.only(bottom: 10), + /// child: Row( + /// crossAxisAlignment: CrossAxisAlignment.end, + /// mainAxisAlignment: MainAxisAlignment.center, + /// children: [ + /// const SizedBox( + /// width: 160, + /// height: 34, + /// child: Center( + /// child: Text( + /// 'Personal calendar', + /// style: TextStyle( + /// fontSize: 16, + /// fontWeight: FontWeight.w900, + /// ), + /// ), + /// ), + /// ), + /// const SizedBox( + /// width: 4, + /// ), + /// Container( + /// width: 160, + /// height: 34, + /// decoration: BoxDecoration( + /// color: const Color(0xFF00273D), + /// borderRadius: const BorderRadius.all( + /// Radius.circular(10), + /// ), + /// boxShadow: [ + /// BoxShadow( + /// color: const Color(0xFF000000).withOpacity(0.50), + /// offset: const Offset(0, 6), + /// blurRadius: 9, + /// ), + /// ], + /// ), + /// child: const Center( + /// child: Text( + /// 'Work calendar', + /// style: TextStyle( + /// color: Colors.white, + /// fontSize: 16, + /// fontWeight: FontWeight.w900, + /// ), + /// ), + /// ), + /// ), + /// ], + /// ), + /// ), + /// child: Container( + /// margin: const EdgeInsets.only( + /// top: 195, + /// ), + /// child: HolidayRoster(), + /// ), + /// ), + ///``` + DateTimePicker({ + this.dateTimePickerTheme = const DateTimePickerTheme(), this.header, this.onTapDay, this.highlightToday = true, + this.wrongTimeDialog, + bool? use24HourFormat, + this.pickTime = false, this.initialDate, this.markedDates, - Key? key, - }) : super(key: key); + this.disabledDates, + this.disabledTimes, + this.child, + super.key, + }) { + alwaysUse24HourFormat = use24HourFormat ?? _useTimeFormatBasedOnLocale(); + } - final Widget child; + /// 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 State createState() => _DateTimePickerState(); } @@ -132,6 +170,9 @@ class DateTimePicker extends StatefulWidget { class _DateTimePickerState extends State { late DateTimePickerController _dateTimePickerController; + final DraggableScrollableController _dragController = + DraggableScrollableController(); + @override void initState() { super.initState(); @@ -139,16 +180,19 @@ class _DateTimePickerState extends State { _dateTimePickerController = DateTimePickerController( highlightToday: widget.highlightToday, + alwaysUse24HourFormat: widget.alwaysUse24HourFormat, + pickTime: widget.pickTime, + theme: widget.dateTimePickerTheme, header: widget.header, markedDates: widget.markedDates, + disabledDates: widget.disabledDates, + disabledTimes: widget.disabledTimes, onTapDayCallBack: widget.onTapDay, browsingDate: widget.initialDate ?? DateTime.now(), selectedDate: widget.initialDate ?? DateTime.now(), ); _dateTimePickerController.addListener(() { - print('BROWSING DATE: ${_dateTimePickerController.browsingDate}'); - print('SELECTED DATE: ${_dateTimePickerController.selectedDate}'); setState(() {}); }); } @@ -156,6 +200,7 @@ class _DateTimePickerState extends State { @override void dispose() { _dateTimePickerController.dispose(); + _dragController.dispose(); super.dispose(); } @@ -163,16 +208,20 @@ class _DateTimePickerState extends State { Widget build(BuildContext context) { return Stack( children: [ - widget.child, + if (widget.child != null) ...[ + widget.child!, + ], RotatedBox( quarterTurns: 2, child: DraggableScrollableSheet( - controller: _dateTimePickerController.getDragController(), + controller: _dragController, snap: true, - minChildSize: 0.26, - initialChildSize: 0.26, - maxChildSize: 0.68, + minChildSize: 0.2, + initialChildSize: 0.2, + maxChildSize: 0.6, builder: (context, scrollController) { + double dragSize = + _dragController.isAttached ? _dragController.size : 0; return RotatedBox( quarterTurns: 2, child: Stack( @@ -197,17 +246,18 @@ class _DateTimePickerState extends State { ), ], ), - child: _dateTimePickerController - .getDragController() - .size < - 0.3 + child: dragSize < 0.3 ? WeekDateTimePickerSheet( dateTimePickerController: _dateTimePickerController, + weekDateBoxSize: widget + .dateTimePickerTheme.weekDateBoxSize, ) : MonthDateTimePickerSheet( dateTimePickerController: _dateTimePickerController, + monthDateBoxSize: widget + .dateTimePickerTheme.monthDateBoxSize, ), ), ), @@ -222,3 +272,24 @@ class _DateTimePickerState extends State { ); } } + +bool _useTimeFormatBasedOnLocale() { + // Get LocaleName of current platform and split language- and countryCode in 2 List values. + List deviceLocale = Platform.localeName.split('_'); + + // Make LocaleName of current platform in a Locale Object + Locale defaultLocale = Locale.fromSubtags( + languageCode: deviceLocale[0], + countryCode: deviceLocale[1], + ); + + // Determine Country. + switch (defaultLocale.countryCode) { + case 'NL': + return true; + case 'US': + return false; + default: + return true; + } +} diff --git a/lib/src/enums/date_box_shape.dart b/lib/src/enums/date_box_shape.dart new file mode 100644 index 0000000..9010f11 --- /dev/null +++ b/lib/src/enums/date_box_shape.dart @@ -0,0 +1,7 @@ + +/// Defines the shape of a specific date. +enum DateBoxShape { + circle, + rectangle, + roundedRectangle +} \ No newline at end of file diff --git a/lib/src/extensions/date_time.dart b/lib/src/extensions/date_time.dart index d48f756..6c82034 100644 --- a/lib/src/extensions/date_time.dart +++ b/lib/src/extensions/date_time.dart @@ -1,16 +1,17 @@ -extension DatePickerUtil on DateTime { - // Check if the current date is the same as the given date - bool sameDayAs(DateTime selectedDate) { +extension DateTimeExtension on DateTime { + /// 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 isContainedIn(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 DatePickerUtil 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 new file mode 100644 index 0000000..0e0faf5 --- /dev/null +++ b/lib/src/extensions/time_of_day.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +extension TimeOfDayExtension on TimeOfDay { + /// Check if the current [TimeOfDay] contains any the given [times] + bool containsAny(List times) { + return times.any((element) => element.equals(this)); + } + + /// 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/models/date_box_base_theme.dart b/lib/src/models/date_box_base_theme.dart new file mode 100644 index 0000000..2dc6283 --- /dev/null +++ b/lib/src/models/date_box_base_theme.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart' show Color, TextStyle; + +class DateBoxBaseTheme { + /// Default date theme. + const DateBoxBaseTheme( + this.backgroundColor, + this.textStyle, + ); + + /// Background color of default date + final Color? backgroundColor; + + /// The style of the date number. + final TextStyle? textStyle; +} diff --git a/lib/src/models/date_box_current_theme.dart b/lib/src/models/date_box_current_theme.dart new file mode 100644 index 0000000..1c1f6d8 --- /dev/null +++ b/lib/src/models/date_box_current_theme.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart' show Color, TextStyle; + +class DateBoxCurrentTheme { + /// Default date theme. + const DateBoxCurrentTheme( + this.backgroundColor, + this.textStyle, + ); + + /// Background color of default date + final Color backgroundColor; + + /// The style of the date number. + final TextStyle textStyle; +} diff --git a/lib/src/models/date_box_disabled_theme.dart b/lib/src/models/date_box_disabled_theme.dart new file mode 100644 index 0000000..7172fe5 --- /dev/null +++ b/lib/src/models/date_box_disabled_theme.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart' show Color, TextStyle; + +class DateBoxDisabledTheme { + /// Disabled date theme. + const DateBoxDisabledTheme( + this.backgroundColor, + this.textStyle, + ); + + /// Background color of selected date. + final Color? backgroundColor; + + /// The style of the date number. + final TextStyle? textStyle; +} diff --git a/lib/src/models/date_box_highlight_theme.dart b/lib/src/models/date_box_highlight_theme.dart new file mode 100644 index 0000000..97a26bd --- /dev/null +++ b/lib/src/models/date_box_highlight_theme.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart' show Color, TextStyle; + +class DateBoxHighlightTheme { + /// Highlighted date theme. + const DateBoxHighlightTheme( + this.backgroundColor, + this.textStyle, + ); + + /// Background color of highlighted date. + final Color? backgroundColor; + + /// The style of the date number. + final TextStyle? textStyle; +} diff --git a/lib/src/models/date_box_selected_theme.dart b/lib/src/models/date_box_selected_theme.dart new file mode 100644 index 0000000..abe73af --- /dev/null +++ b/lib/src/models/date_box_selected_theme.dart @@ -0,0 +1,15 @@ +import 'package:flutter/widgets.dart' show Color, TextStyle; + +class DateBoxSelectedTheme { + /// Selected date theme. + const DateBoxSelectedTheme( + this.backgroundColor, + this.textStyle, + ); + + /// Background color of selected date. + final Color? backgroundColor; + + /// The style of the date number. + final TextStyle? textStyle; +} diff --git a/lib/src/models/date_time_picker_theme.dart b/lib/src/models/date_time_picker_theme.dart new file mode 100644 index 0000000..b8ec3a2 --- /dev/null +++ b/lib/src/models/date_time_picker_theme.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/flutter_date_time_picker.dart'; + +class DateTimePickerTheme { + /// The [DateTimePickerTheme] to style [DateTimePicker] in. Define a custom shape for the dates and specifically style + /// a basic, hightlighted, selected and disabled date. + const DateTimePickerTheme({ + this.weekDateBoxSize = 35, + this.monthDateBoxSize = 45, + this.markedIndicatorColor, + this.dateBoxShape = DateBoxShape.roundedRectangle, + this.baseTheme = const DateBoxBaseTheme( + Colors.white, + TextStyle(color: Colors.black), + ), + this.highlightTheme = const DateBoxHighlightTheme( + Colors.blue, + TextStyle(color: Colors.white), + ), + this.selectedTheme = const DateBoxSelectedTheme( + Color(0xFFBBDEFB), + TextStyle(color: Colors.blue), + ), + this.disabledTheme = const DateBoxDisabledTheme( + Colors.grey, + TextStyle(color: Colors.white), + ), + }); + + /// enum to define a shape dor the date. use [DateBoxShape.circle]. + /// For a ciruclar date, [DateBoxShape.rectangle] for a plain box and [DateBoxShape.roundedRectangle] to het a rectangle with small rounded borders. + final DateBoxShape dateBoxShape; + + /// This theme is used to style a default look for the dates. + final DateBoxBaseTheme baseTheme; + + /// This theme is used for when a specific date is highlighted. + final DateBoxHighlightTheme highlightTheme; + + /// This theme is used for when a specific date is slected by the user. + final DateBoxSelectedTheme selectedTheme; + + /// This theme is used for when a specific date is disabled. + final DateBoxDisabledTheme disabledTheme; + + /// Size of date box in a week view. + final double weekDateBoxSize; + + /// Size of date box in a month view. + final double monthDateBoxSize; + + /// The color used for a indicator for a marked date. + final Color? markedIndicatorColor; +} diff --git a/lib/src/utils/date_time_picker_controller.dart b/lib/src/utils/date_time_picker_controller.dart index e27e905..ae44af8 100644 --- a/lib/src/utils/date_time_picker_controller.dart +++ b/lib/src/utils/date_time_picker_controller.dart @@ -1,24 +1,37 @@ import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/src/models/date_time_picker_theme.dart'; class DateTimePickerController extends ChangeNotifier { DateTimePickerController({ + required this.theme, required this.highlightToday, - this.header, - this.markedDates, - this.onTapDayCallBack, + required this.alwaysUse24HourFormat, + required this.pickTime, required this.browsingDate, required this.selectedDate, + this.header, + this.wrongTimeDialog, + this.markedDates, + this.disabledDates, + this.disabledTimes, + this.onTapDayCallBack, }); - final DraggableScrollableController _dragController = - DraggableScrollableController(); final PageController _pageController = PageController(initialPage: 1); final bool highlightToday; + final bool? alwaysUse24HourFormat; final Widget? header; + final Widget? wrongTimeDialog; + + final DateTimePickerTheme theme; + final List? markedDates; + final List? disabledDates; + final List? disabledTimes; + final bool pickTime; final Function(DateTime)? onTapDayCallBack; @@ -28,7 +41,6 @@ class DateTimePickerController extends ChangeNotifier { @override void dispose() { _pageController.dispose(); - _dragController.dispose(); super.dispose(); } @@ -45,7 +57,7 @@ class DateTimePickerController extends ChangeNotifier { ); } - void onTapDay(date) { + void onTapDay(DateTime date) { browsingDate = date; selectedDate = date; @@ -58,13 +70,7 @@ class DateTimePickerController extends ChangeNotifier { } } - DraggableScrollableController getDragController() { - return _dragController; - } - - PageController getPageController() { - return _pageController; - } + PageController get pageController => _pageController; void setBrowsingDate(DateTime date) { browsingDate = date; 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 b548487..593fb28 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 @@ -1,16 +1,22 @@ import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/src/enums/date_box_shape.dart'; import 'package:flutter_date_time_picker/src/extensions/date_time.dart'; +import 'package:flutter_date_time_picker/src/extensions/time_of_day.dart'; +import 'package:flutter_date_time_picker/src/models/date_box_current_theme.dart'; +import 'package:flutter_date_time_picker/src/models/date_time_picker_theme.dart'; import 'package:flutter_date_time_picker/src/utils/date_time_picker_controller.dart'; class MonthDateTimePicker extends StatelessWidget { const MonthDateTimePicker({ required this.date, required this.dateTimePickerController, + required this.monthDateBoxSize, Key? key, }) : super(key: key); final DateTime date; final DateTimePickerController dateTimePickerController; + final double monthDateBoxSize; @override Widget build(BuildContext context) { @@ -26,101 +32,104 @@ class MonthDateTimePicker extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 30), child: Center( child: GridView.count( + physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 5, crossAxisCount: 7, children: List.generate( - DateTime(date.year, date.month).daysInMonth() + daysToSkip, + DateTime(date.year, date.month).daysInMonth() + + (daysToSkip >= 7 ? 0 : daysToSkip), (index) { - if (index < daysToSkip) { - return Container( - decoration: BoxDecoration( - color: Colors.red.withOpacity(0.4), - border: Border.all(color: Colors.black, width: 1.5), - ), - margin: - const EdgeInsets.symmetric(vertical: 5, horizontal: 5), - height: 45, - width: 45, - child: Center( - child: Text( - (index + 1 - daysToSkip).toString(), - style: Theme.of(context).textTheme.bodyText1!.copyWith( - color: Colors.black, - ), - ), - ), - ); + late DateBoxCurrentTheme currentDateBoxTheme; + + int addedIndex = index; + + if (daysToSkip >= 7) { + addedIndex = index + 7; + } + if (addedIndex < daysToSkip) { + return const SizedBox.shrink(); } + currentDateBoxTheme = determineCurrentDateBoxTheme(context, + addedIndex, daysToSkip, dateTimePickerController.theme); + return GestureDetector( + onTap: isDisabled( + addedIndex, + daysToSkip, + ) + ? null + : () async { + TimeOfDay? timeOfDay; - onTap: () async { - // await dateTimePickerController.getDragController().animateTo( - // 0.26, - // duration: const Duration( - // milliseconds: 350, - // ), - // curve: Curves.ease, - // ); + DateTime selectedDate = DateTime( + date.year, + date.month, + addedIndex + 1 - daysToSkip, + ); - dateTimePickerController.onTapDay(DateTime( - date.year, - date.month, - index + 1 - daysToSkip, - date.hour, - date.minute, - date.second, - )); - }, + timeOfDay = const TimeOfDay(hour: 0, minute: 0); + + if (dateTimePickerController.pickTime) { + timeOfDay = await displayTimePicker( + context, dateTimePickerController); + } + + if (dateTimePickerController.wrongTimeDialog != null) { + if (timeOfDay != null && + timeOfDay.containsAny( + dateTimePickerController.disabledTimes ?? [], + )) { + showDialog( + context: context, + builder: (context) => + dateTimePickerController.wrongTimeDialog!); + } + } + + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + timeOfDay!.hour, + timeOfDay.minute, + ); + + dateTimePickerController.onTapDay(selectedDateTime); + }, child: Container( margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), decoration: BoxDecoration( - color: - // isDisabled() - // ? Theme.of(context).disabledColor - // : Colors.transparent, - // isSelected(index, daysToSkip) - // ? Theme.of(context).primaryColor.withOpacity(0.2) - // : Colors.transparent, - shouldHighlight(index, daysToSkip) - ? Theme.of(context).primaryColor - : Colors.transparent, - borderRadius: BorderRadius.circular( - 10, - ), + color: currentDateBoxTheme.backgroundColor, + borderRadius: + _determineBorderRadius(dateTimePickerController), ), - height: 45, - width: 45, + height: monthDateBoxSize, + width: monthDateBoxSize, child: Stack( children: [ Center( - child: Text( - (index + 1 - daysToSkip).toString(), - style: - Theme.of(context).textTheme.bodyText1!.copyWith( - color: - // isDisabled() - // ? Colors.white - // : Colors.transparent, - // isSelected(index, daysToSkip) - // ? Theme.of(context).primaryColor - // : Colors.black, - shouldHighlight(index, daysToSkip) - ? Colors.white - : Colors.black, - ), - ), + child: Text((addedIndex + 1 - daysToSkip).toString(), + style: currentDateBoxTheme.textStyle), ), - if (shouldMark(index, daysToSkip)) ...[ + if (shouldMark( + addedIndex, + daysToSkip, + )) ...[ Align( alignment: Alignment.bottomRight, - child: Container( - width: 12, - height: 12, - decoration: BoxDecoration( - color: Theme.of(context).indicatorColor, - borderRadius: BorderRadius.circular(45), + child: IgnorePointer( + child: Container( + width: monthDateBoxSize / 4, + height: monthDateBoxSize / 4, + decoration: BoxDecoration( + color: dateTimePickerController + .theme.markedIndicatorColor ?? + Theme.of(context).indicatorColor, + borderRadius: BorderRadius.circular( + (monthDateBoxSize / 4) * 2), + ), ), ), ), @@ -141,15 +150,56 @@ class MonthDateTimePicker extends StatelessWidget { date.year, date.month, index + 1 - daysToSkip, - ).sameDayAs( + ).equals( dateTimePickerController.highlightToday ? DateTime.now() : dateTimePickerController.selectedDate, ); } - bool isDisabled() { - return true; + DateBoxCurrentTheme determineCurrentDateBoxTheme( + BuildContext context, + int index, + int daysToSkip, + DateTimePickerTheme theme, + ) { + DateBoxCurrentTheme determinedTheme = DateBoxCurrentTheme( + theme.baseTheme.backgroundColor ?? Colors.transparent, + theme.baseTheme.textStyle ?? const TextStyle(color: Colors.black), + ); + + if (isDisabled(index, daysToSkip)) { + determinedTheme = DateBoxCurrentTheme( + theme.disabledTheme.backgroundColor ?? Theme.of(context).disabledColor, + theme.disabledTheme.textStyle ?? const TextStyle(color: Colors.white), + ); + } + if (isSelected(index, daysToSkip)) { + determinedTheme = DateBoxCurrentTheme( + theme.selectedTheme.backgroundColor ?? + Theme.of(context).primaryColor.withOpacity(0.3), + theme.selectedTheme.textStyle ?? + TextStyle(color: Theme.of(context).primaryColor)); + } + if (shouldHighlight(index, daysToSkip)) { + determinedTheme = DateBoxCurrentTheme( + theme.highlightTheme.backgroundColor ?? + Theme.of(context).primaryColor, + theme.highlightTheme.textStyle ?? + const TextStyle(color: Colors.white)); + } + + return determinedTheme; + } + + bool isDisabled(int index, int daysToSkip) { + return DateTime( + date.year, + date.month, + index + 1 - daysToSkip, + ).containsAny( + dateTimePickerController.disabledDates ?? [], + ); } bool isSelected(int index, int daysToSkip) { @@ -157,7 +207,7 @@ class MonthDateTimePicker extends StatelessWidget { date.year, date.month, index + 1 - daysToSkip, - ).sameDayAs(dateTimePickerController.selectedDate); + ).equals(dateTimePickerController.selectedDate); } bool shouldMark(int index, int daysToSkip) { @@ -165,7 +215,7 @@ class MonthDateTimePicker extends StatelessWidget { date.year, date.month, index + 1 - daysToSkip, - ).sameDayAs( + ).equals( dateTimePickerController.highlightToday ? DateTime.now() : dateTimePickerController.selectedDate, @@ -174,8 +224,35 @@ class MonthDateTimePicker extends StatelessWidget { date.year, date.month, index + 1 - daysToSkip, - ).isContainedIn( + ).containsAny( dateTimePickerController.markedDates ?? [], ); } + + BorderRadius _determineBorderRadius( + DateTimePickerController dateTimePickerController) { + switch (dateTimePickerController.theme.dateBoxShape) { + case DateBoxShape.circle: + return BorderRadius.circular(monthDateBoxSize * 2); + case DateBoxShape.rectangle: + return BorderRadius.zero; + case DateBoxShape.roundedRectangle: + return BorderRadius.circular(monthDateBoxSize / 4.5); + } + } +} + +displayTimePicker(BuildContext context, + DateTimePickerController dateTimePickerController) async { + return await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + builder: (BuildContext context, Widget? child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: + dateTimePickerController.alwaysUse24HourFormat), + child: child!, + ); + }); } diff --git a/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker_sheet.dart b/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker_sheet.dart index 6a08cd7..5a80632 100644 --- a/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker_sheet.dart +++ b/lib/src/widgets/month_date_time_picker.dart/month_date_time_picker_sheet.dart @@ -7,10 +7,12 @@ import 'package:intl/intl.dart'; class MonthDateTimePickerSheet extends StatelessWidget { const MonthDateTimePickerSheet({ required this.dateTimePickerController, + required this.monthDateBoxSize, Key? key, }) : super(key: key); final DateTimePickerController dateTimePickerController; + final double monthDateBoxSize; @override Widget build(BuildContext context) { @@ -28,16 +30,14 @@ class MonthDateTimePickerSheet extends StatelessWidget { DateFormat.yMMMM().format( dateTimePickerController.browsingDate, ), - style: Theme.of(context) - .textTheme - .headlineMedium! - .copyWith(color: Colors.black), + style: dateTimePickerController.theme.baseTheme.textStyle! + .copyWith(fontSize: 25), ), SizedBox( width: MediaQuery.of(context).size.width, - height: 300, + height: MediaQuery.of(context).size.height * 0.33, child: PageView( - controller: dateTimePickerController.getPageController(), + controller: dateTimePickerController.pageController, onPageChanged: (i) { if (i == 0) { dateTimePickerController.onPageChanged( @@ -68,21 +68,27 @@ class MonthDateTimePickerSheet extends StatelessWidget { dateTimePickerController.browsingDate.year - 1, 12, 1) : DateTime(dateTimePickerController.browsingDate.year, dateTimePickerController.browsingDate.month - 1, 1), + monthDateBoxSize: monthDateBoxSize, dateTimePickerController: dateTimePickerController), MonthDateTimePicker( - date: dateTimePickerController.browsingDate, - dateTimePickerController: dateTimePickerController), + date: DateTime( + dateTimePickerController.browsingDate.year, + dateTimePickerController.browsingDate.month, + 1, + ), + dateTimePickerController: dateTimePickerController, + monthDateBoxSize: monthDateBoxSize), MonthDateTimePicker( - date: dateTimePickerController.browsingDate.month == 12 - ? DateTime( - dateTimePickerController.browsingDate.year + 1, - 1, - 1, - ) - : DateTime(dateTimePickerController.browsingDate.year, - dateTimePickerController.browsingDate.month + 1, 1), - dateTimePickerController: dateTimePickerController, - ), + date: dateTimePickerController.browsingDate.month == 12 + ? DateTime( + dateTimePickerController.browsingDate.year + 1, + 1, + 1, + ) + : DateTime(dateTimePickerController.browsingDate.year, + dateTimePickerController.browsingDate.month + 1, 1), + dateTimePickerController: dateTimePickerController, + monthDateBoxSize: monthDateBoxSize), ], ), ), 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 dd61730..9cf8778 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 @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/flutter_date_time_picker.dart'; import 'package:flutter_date_time_picker/src/extensions/date_time.dart'; +import 'package:flutter_date_time_picker/src/extensions/time_of_day.dart'; +import 'package:flutter_date_time_picker/src/models/date_box_current_theme.dart'; import 'package:flutter_date_time_picker/src/utils/date_time_picker_controller.dart'; import 'package:intl/intl.dart'; @@ -7,6 +10,7 @@ class WeekDateTimePicker extends StatelessWidget { const WeekDateTimePicker({ required this.dateTimePickerController, required this.date, + required this.weekDateBoxSize, Key? key, }) : super(key: key); @@ -14,82 +18,155 @@ class WeekDateTimePicker extends StatelessWidget { final DateTime date; + final double weekDateBoxSize; + @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate( date.daysOfWeek().length, - (index) => GestureDetector( - onTap: () { - dateTimePickerController.onTapDay(date.daysOfWeek()[index]); - }, - child: SizedBox( - width: 40, - height: 60, - child: Column( - children: [ - const Spacer(), - Text( - DateFormat.E() - .format( - date.daysOfWeek().elementAt(index), - ) - .toUpperCase()[0], - style: Theme.of(context).textTheme.titleSmall, - ), - const Spacer(), - Container( - height: 35, - width: 35, - decoration: BoxDecoration( - color: shouldHighlight(index) - ? Theme.of(context).primaryColor - : Colors.transparent, - borderRadius: BorderRadius.circular( - 10, - ), + (index) { + late DateBoxCurrentTheme currentDateBoxTheme; + + currentDateBoxTheme = determineCurrentDateBoxTheme( + context, index, dateTimePickerController.theme); + return GestureDetector( + onTap: isDisabled( + index, + ) + ? null + : () async { + TimeOfDay? timeOfDay; + + 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.containsAny( + dateTimePickerController.disabledTimes ?? [], + )) { + showDialog( + context: context, + builder: (context) => + dateTimePickerController.wrongTimeDialog!, + ); + } + } + + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + timeOfDay!.hour, + timeOfDay.minute, + ); + + dateTimePickerController.onTapDay(selectedDateTime); + }, + child: SizedBox( + width: 40, + height: 60, + child: Column( + children: [ + const Spacer(), + Text( + DateFormat.E() + .format( + date.daysOfWeek().elementAt(index), + ) + .toUpperCase()[0], + style: dateTimePickerController.theme.baseTheme.textStyle, ), - child: Stack( - children: [ - Center( - child: Text( - date.daysOfWeek().elementAt(index).day.toString(), - style: - Theme.of(context).textTheme.bodyMedium!.copyWith( - color: shouldHighlight(index) - ? Colors.white - : Colors.black, - ), - ), - ), - if (shouldMark(index)) ...[ - Align( - alignment: Alignment.bottomRight, - child: Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: Theme.of(context).indicatorColor, - borderRadius: BorderRadius.circular(45), - ), + const Spacer(), + Container( + height: weekDateBoxSize, + width: weekDateBoxSize, + decoration: BoxDecoration( + color: currentDateBoxTheme.backgroundColor, + borderRadius: + _determineBorderRadius(dateTimePickerController), + ), + child: Stack( + children: [ + Center( + child: Text( + date.daysOfWeek().elementAt(index).day.toString(), + style: currentDateBoxTheme.textStyle, ), ), + if (shouldMark(index)) ...[ + Align( + alignment: Alignment.bottomRight, + child: Container( + width: weekDateBoxSize / 3, + height: weekDateBoxSize / 3, + decoration: BoxDecoration( + color: dateTimePickerController + .theme.markedIndicatorColor ?? + Theme.of(context).indicatorColor, + borderRadius: + BorderRadius.circular(weekDateBoxSize * 2), + ), + ), + ), + ], ], - ], + ), ), - ), - const Spacer(), - ], + const Spacer(), + ], + ), ), - ), - ), + ); + }, ), ); } + DateBoxCurrentTheme determineCurrentDateBoxTheme( + BuildContext context, + int index, + DateTimePickerTheme theme, + ) { + DateBoxCurrentTheme determinedTheme = DateBoxCurrentTheme( + theme.baseTheme.backgroundColor ?? Colors.transparent, + theme.baseTheme.textStyle ?? const TextStyle(color: Colors.black), + ); + + if (isDisabled(index)) { + determinedTheme = DateBoxCurrentTheme( + theme.disabledTheme.backgroundColor ?? Theme.of(context).disabledColor, + theme.disabledTheme.textStyle ?? const TextStyle(color: Colors.white), + ); + } + if (isSelected(index)) { + determinedTheme = DateBoxCurrentTheme( + theme.selectedTheme.backgroundColor ?? + Theme.of(context).primaryColor.withOpacity(0.3), + theme.selectedTheme.textStyle ?? + TextStyle(color: Theme.of(context).primaryColor)); + } + if (shouldHighlight(index)) { + determinedTheme = DateBoxCurrentTheme( + theme.highlightTheme.backgroundColor ?? + Theme.of(context).primaryColor, + theme.highlightTheme.textStyle ?? + const TextStyle(color: Colors.white)); + } + + return determinedTheme; + } + bool shouldHighlight(int index) { - return date.daysOfWeek().elementAt(index).sameDayAs( + return date.daysOfWeek().elementAt(index).equals( dateTimePickerController.highlightToday ? DateTime.now() : dateTimePickerController.selectedDate, @@ -100,11 +177,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) + .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, @@ -112,6 +196,33 @@ class WeekDateTimePicker extends StatelessWidget { date .daysOfWeek() .elementAt(index) - .isContainedIn(dateTimePickerController.markedDates ?? []); + .containsAny(dateTimePickerController.markedDates ?? []); + } + + BorderRadius _determineBorderRadius( + DateTimePickerController dateTimePickerController) { + switch (dateTimePickerController.theme.dateBoxShape) { + case DateBoxShape.circle: + return BorderRadius.circular(weekDateBoxSize * 2); + case DateBoxShape.rectangle: + return BorderRadius.zero; + case DateBoxShape.roundedRectangle: + return BorderRadius.circular(weekDateBoxSize / 4.5); + } + } + + displayTimePicker(BuildContext context, + DateTimePickerController dateTimePickerController) async { + return await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + builder: (BuildContext context, Widget? child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: + dateTimePickerController.alwaysUse24HourFormat), + child: child!, + ); + }); } } diff --git a/lib/src/widgets/week_date_time_picker/week_date_time_picker_sheet.dart b/lib/src/widgets/week_date_time_picker/week_date_time_picker_sheet.dart index d6c5b48..d7bf1ce 100644 --- a/lib/src/widgets/week_date_time_picker/week_date_time_picker_sheet.dart +++ b/lib/src/widgets/week_date_time_picker/week_date_time_picker_sheet.dart @@ -7,12 +7,14 @@ import 'package:intl/intl.dart'; class WeekDateTimePickerSheet extends StatelessWidget { const WeekDateTimePickerSheet({ required this.dateTimePickerController, + required this.weekDateBoxSize, this.showHeader = false, Key? key, }) : super(key: key); final DateTimePickerController dateTimePickerController; final bool showHeader; + final double weekDateBoxSize; String getDateHeader() { List weekDays = @@ -43,7 +45,8 @@ class WeekDateTimePickerSheet extends StatelessWidget { if (showHeader) ...[ Text( getDateHeader(), - style: Theme.of(context).textTheme.headlineSmall, + style: dateTimePickerController.theme.baseTheme.textStyle! + .copyWith(fontSize: 9), ), const SizedBox( height: 10, @@ -53,7 +56,7 @@ class WeekDateTimePickerSheet extends StatelessWidget { width: MediaQuery.of(context).size.width, height: 60, child: PageView( - controller: dateTimePickerController.getPageController(), + controller: dateTimePickerController.pageController, onPageChanged: (i) { if (i == 0) { dateTimePickerController.onPageChanged( @@ -75,16 +78,19 @@ class WeekDateTimePickerSheet extends StatelessWidget { date: dateTimePickerController.browsingDate.subtract( const Duration(days: 7), ), + weekDateBoxSize: weekDateBoxSize, ), WeekDateTimePicker( dateTimePickerController: dateTimePickerController, date: dateTimePickerController.browsingDate, + weekDateBoxSize: weekDateBoxSize, ), WeekDateTimePicker( dateTimePickerController: dateTimePickerController, date: dateTimePickerController.browsingDate.add( const Duration(days: 7), ), + weekDateBoxSize: weekDateBoxSize, ), ], ), diff --git a/test/date_time_extension_test.dart b/test/date_time_extension_test.dart new file mode 100644 index 0000000..805d3b4 --- /dev/null +++ b/test/date_time_extension_test.dart @@ -0,0 +1,75 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_date_time_picker/src/extensions/date_time.dart'; + +void main() { + group('DateTimeExtension', () { + 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( + 'containsAny() should return a boolean if the date is found in a list of dates or not', + () { + expect( + DateTime(2022, 01, 01).containsAny([ + DateTime(2022, 01, 01), + DateTime(2022, 01, 02), + DateTime(2022, 01, 03) + ]), + true); + + expect( + DateTime(2022, 01, 01).containsAny([ + DateTime(2022, 01, 02), + DateTime(2022, 01, 03), + DateTime(2022, 01, 04) + ]), + false); + }); + + test( + 'isLeapYear should return a boolean if the given year is a leap year or not', + () { + expect(DateTime(2016, 01, 01).isLeapYear, true); // A leap year + expect(DateTime(2017, 01, 01).isLeapYear, false); // Not a leap year + expect(DateTime(2018, 01, 01).isLeapYear, false); // Not a leap year + expect(DateTime(2019, 01, 01).isLeapYear, false); // Not a leap year + expect(DateTime(2020, 01, 01).isLeapYear, true); // A leap year + }); + + test( + 'daysInMonth() should return the amount of days in the month relative on the date', + () { + var monthOfJanuary = DateTime(2022, 01, 01); + var monthOfFebruary = DateTime(2022, 02, 01); + var monthOfMarch = DateTime(2022, 03, 01); + var monthOfApril = DateTime(2022, 04, 01); + var monthOfMay = DateTime(2022, 05, 01); + var monthOfJune = DateTime(2022, 06, 01); + var monthOfJuly = DateTime(2022, 07, 01); + var monthOfAugust = DateTime(2022, 08, 01); + var monthOfSeptember = DateTime(2022, 09, 01); + var monthOfOctober = DateTime(2022, 10, 01); + var monthOfNovember = DateTime(2022, 11, 01); + var monthOfDecember = DateTime(2022, 12, 01); + + expect(monthOfJanuary.daysInMonth(), 31); + // 2022 is not a leap year so this should return 28 + expect(monthOfFebruary.daysInMonth(), 28); + expect(monthOfMarch.daysInMonth(), 31); + expect(monthOfApril.daysInMonth(), 30); + expect(monthOfMay.daysInMonth(), 31); + expect(monthOfJune.daysInMonth(), 30); + expect(monthOfJuly.daysInMonth(), 31); + expect(monthOfAugust.daysInMonth(), 31); + expect(monthOfSeptember.daysInMonth(), 30); + expect(monthOfOctober.daysInMonth(), 31); + expect(monthOfNovember.daysInMonth(), 30); + expect(monthOfDecember.daysInMonth(), 31); + + // 2020 is a leap year so this should return 29 + expect(DateTime(2020, 02, 01).daysInMonth(), 29); + }); + }); +} diff --git a/test/flutter_date_time_picker_test.dart b/test/flutter_date_time_picker_test.dart index 2670ee4..b886144 100644 --- a/test/flutter_date_time_picker_test.dart +++ b/test/flutter_date_time_picker_test.dart @@ -1,12 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/flutter_date_time_picker.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_date_time_picker/flutter_date_time_picker.dart'; - void main() { - test('adds one to input values', () { - final calculator = Calculator(); - expect(calculator.addOne(2), 3); - expect(calculator.addOne(-7), -6); - expect(calculator.addOne(0), 1); + testWidgets('Render App with DateTimePicker Widget', (tester) async { + // Render App + await tester.pumpWidget(MaterialApp( + home: Scaffold( + appBar: AppBar(), + body: DateTimePicker( + pickTime: false, + child: Container(), + ), + ), + )); + await tester.pump(); + }); + + testWidgets('Test if DateTimePicker Widget swipes', (tester) async { + // Render App + await tester.pumpWidget(MaterialApp( + home: Scaffold( + appBar: AppBar(), + body: DateTimePicker( + pickTime: false, + child: Container(), + ), + ), + )); + await tester.pump(); + + // Forward + await tester.drag( + find.byType(SingleChildScrollView), const Offset(500.0, 0.0)); + await tester.pumpAndSettle(); + + // Return + await tester.drag( + find.byType(SingleChildScrollView), const Offset(-500.0, 0.0)); + await tester.pumpAndSettle(); + + // Backward + await tester.drag( + find.byType(SingleChildScrollView), const Offset(-500.0, 0.0)); + await tester.pumpAndSettle(); }); } diff --git a/test/time_of_day_extension_test.dart b/test/time_of_day_extension_test.dart new file mode 100644 index 0000000..4e2b1f8 --- /dev/null +++ b/test/time_of_day_extension_test.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_date_time_picker/src/extensions/time_of_day.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('TimeOfDayExtension', () { + test('equals() should return true if the same time, if not false', () { + expect( + const TimeOfDay(hour: 12, minute: 0) + .equals(const TimeOfDay(hour: 12, minute: 0)), + true); + expect( + const TimeOfDay(hour: 13, minute: 0) + .equals(const TimeOfDay(hour: 12, minute: 0)), + false); + }); + + test( + 'containsAny() should return a boolean if the time is found in a list of times or not', + () { + expect( + const TimeOfDay(hour: 12, minute: 0).containsAny(const [ + TimeOfDay(hour: 10, minute: 0), + TimeOfDay(hour: 11, minute: 0), + TimeOfDay(hour: 12, minute: 0) + ]), + true); + + expect( + const TimeOfDay(hour: 12, minute: 0).containsAny(const [ + TimeOfDay(hour: 9, minute: 0), + TimeOfDay(hour: 10, minute: 0), + TimeOfDay(hour: 11, minute: 0) + ]), + false); + }); + }); +}