1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'calendar_date_picker.dart';
6/// @docImport 'date_picker.dart';
7/// @docImport 'text_field.dart';
8library;
9
10import 'package:flutter/foundation.dart';
11import 'package:flutter/widgets.dart';
12
13import 'material_localizations.dart';
14
15/// Controls the calendar system used in the date picker.
16///
17/// A [CalendarDelegate] defines how dates are interpreted, formatted, and
18/// navigated within the picker. Different calendar systems (e.g., Gregorian,
19/// Nepali, Hijri, Buddhist) can be supported by providing custom implementations.
20///
21/// {@tool dartpad}
22/// This example demonstrates how a [CalendarDelegate] is used to implement a
23/// custom calendar system in the date picker.
24///
25/// ** See code in examples/api/lib/material/date_picker/custom_calendar_date_picker.0.dart **
26/// {@end-tool}
27///
28/// See also:
29///
30/// * [GregorianCalendarDelegate], the default implementation for the Gregorian calendar.
31/// * [CalendarDatePicker], which uses this delegate to manage calendar-specific behavior.
32abstract class CalendarDelegate<T extends DateTime> {
33 /// Creates a calendar delegate.
34 const CalendarDelegate();
35
36 /// Returns a [DateTime] representing the current date and time.
37 T now();
38
39 /// {@macro flutter.material.date.dateOnly}
40 T dateOnly(T date);
41
42 /// {@macro flutter.material.date.datesOnly}
43 DateTimeRange<T> datesOnly(DateTimeRange<T> range) {
44 return DateTimeRange<T>(start: dateOnly(range.start), end: dateOnly(range.end));
45 }
46
47 /// {@macro flutter.material.date.isSameDay}
48 bool isSameDay(T? dateA, T? dateB) {
49 return dateA?.year == dateB?.year && dateA?.month == dateB?.month && dateA?.day == dateB?.day;
50 }
51
52 /// {@macro flutter.material.date.isSameMonth}
53 bool isSameMonth(T? dateA, T? dateB) {
54 return dateA?.year == dateB?.year && dateA?.month == dateB?.month;
55 }
56
57 /// {@macro flutter.material.date.monthDelta}
58 int monthDelta(T startDate, T endDate);
59
60 /// {@macro flutter.material.date.addMonthsToMonthDate}
61 T addMonthsToMonthDate(T monthDate, int monthsToAdd);
62
63 /// {@macro flutter.material.date.addDaysToDate}
64 T addDaysToDate(T date, int days);
65
66 /// {@macro flutter.material.date.firstDayOffset}
67 int firstDayOffset(int year, int month, MaterialLocalizations localizations);
68
69 /// Returns the number of days in a month, according to the calendar system.
70 int getDaysInMonth(int year, int month);
71
72 /// Returns a [DateTime] with the given [year] and [month].
73 T getMonth(int year, int month);
74
75 /// Returns a [DateTime] with the given [year], [month], and [day].
76 T getDay(int year, int month, int day);
77
78 /// Formats the month and the year of the given [date].
79 ///
80 /// The returned string does not contain the day of the month. This appears
81 /// in the date picker invoked using [showDatePicker].
82 String formatMonthYear(T date, MaterialLocalizations localizations);
83
84 /// Full unabbreviated year format, e.g. 2017 rather than 17.
85 String formatYear(int year, MaterialLocalizations localizations) {
86 return localizations.formatYear(DateTime(year));
87 }
88
89 /// Formats the date using a medium-width format.
90 ///
91 /// Abbreviates month and days of week. This appears in the header of the date
92 /// picker invoked using [showDatePicker].
93 ///
94 /// Examples:
95 ///
96 /// - US English: Wed, Sep 27
97 /// - Russian: ср, сент. 27
98 String formatMediumDate(T date, MaterialLocalizations localizations);
99
100 /// Formats the month and day of the given [date].
101 ///
102 /// Examples:
103 ///
104 /// - US English: Feb 21
105 /// - Russian: 21 февр.
106 String formatShortMonthDay(T date, MaterialLocalizations localizations);
107
108 /// Formats the date using a short-width format.
109 ///
110 /// Includes the abbreviation of the month, the day and year.
111 ///
112 /// Examples:
113 ///
114 /// - US English: Feb 21, 2019
115 /// - Russian: 21 февр. 2019 г.
116 String formatShortDate(T date, MaterialLocalizations localizations);
117
118 /// Formats day of week, month, day of month and year in a long-width format.
119 ///
120 /// Does not abbreviate names. Appears in spoken announcements of the date
121 /// picker invoked using [showDatePicker], when accessibility mode is on.
122 ///
123 /// Examples:
124 ///
125 /// - US English: Wednesday, September 27, 2017
126 /// - Russian: Среда, Сентябрь 27, 2017
127 String formatFullDate(T date, MaterialLocalizations localizations);
128
129 /// Formats the date in a compact format.
130 ///
131 /// Usually just the numeric values for the for day, month and year are used.
132 ///
133 /// Examples:
134 ///
135 /// - US English: 02/21/2019
136 /// - Russian: 21.02.2019
137 ///
138 /// See also:
139 /// * [parseCompactDate], which will convert a compact date string to a [DateTime].
140 String formatCompactDate(T date, MaterialLocalizations localizations);
141
142 /// Converts the given compact date formatted string into a [DateTime].
143 ///
144 /// The format of the string must be a valid compact date format for the
145 /// given locale. If the text doesn't represent a valid date, `null` will be
146 /// returned.
147 ///
148 /// See also:
149 /// * [formatCompactDate], which will convert a [DateTime] into a string in the compact format.
150 T? parseCompactDate(String? inputString, MaterialLocalizations localizations);
151
152 /// The help text used on an empty [InputDatePickerFormField] to indicate
153 /// to the user the date format being asked for.
154 String dateHelpText(MaterialLocalizations localizations);
155}
156
157/// A [CalendarDelegate] implementation for the Gregorian calendar system.
158///
159/// The Gregorian calendar is the most widely used civil calendar worldwide.
160/// This delegate provides standard date interpretation, formatting, and
161/// navigation based on the Gregorian system.
162///
163/// This delegate is the default calendar system for [CalendarDatePicker].
164///
165/// See also:
166/// * [CalendarDelegate], the base class for defining custom calendars.
167/// * [CalendarDatePicker], which uses this delegate for date selection.
168class GregorianCalendarDelegate extends CalendarDelegate<DateTime> {
169 /// Creates a calendar delegate that uses the Gregorian calendar and the
170 /// conventions of the current [MaterialLocalizations].
171 const GregorianCalendarDelegate();
172
173 @override
174 DateTime now() => DateTime.now();
175
176 @override
177 DateTime dateOnly(DateTime date) => DateUtils.dateOnly(date);
178
179 @override
180 int monthDelta(DateTime startDate, DateTime endDate) => DateUtils.monthDelta(startDate, endDate);
181
182 @override
183 DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) {
184 return DateUtils.addMonthsToMonthDate(monthDate, monthsToAdd);
185 }
186
187 @override
188 DateTime addDaysToDate(DateTime date, int days) => DateUtils.addDaysToDate(date, days);
189
190 @override
191 int firstDayOffset(int year, int month, MaterialLocalizations localizations) {
192 return DateUtils.firstDayOffset(year, month, localizations);
193 }
194
195 /// {@macro flutter.material.date.getDaysInMonth}
196 @override
197 int getDaysInMonth(int year, int month) => DateUtils.getDaysInMonth(year, month);
198
199 @override
200 DateTime getMonth(int year, int month) => DateTime(year, month);
201
202 @override
203 DateTime getDay(int year, int month, int day) => DateTime(year, month, day);
204
205 @override
206 String formatMonthYear(DateTime date, MaterialLocalizations localizations) {
207 return localizations.formatMonthYear(date);
208 }
209
210 @override
211 String formatMediumDate(DateTime date, MaterialLocalizations localizations) {
212 return localizations.formatMediumDate(date);
213 }
214
215 @override
216 String formatShortMonthDay(DateTime date, MaterialLocalizations localizations) {
217 return localizations.formatShortMonthDay(date);
218 }
219
220 @override
221 String formatShortDate(DateTime date, MaterialLocalizations localizations) {
222 return localizations.formatShortDate(date);
223 }
224
225 @override
226 String formatFullDate(DateTime date, MaterialLocalizations localizations) {
227 return localizations.formatFullDate(date);
228 }
229
230 @override
231 String formatCompactDate(DateTime date, MaterialLocalizations localizations) {
232 return localizations.formatCompactDate(date);
233 }
234
235 @override
236 DateTime? parseCompactDate(String? inputString, MaterialLocalizations localizations) {
237 return localizations.parseCompactDate(inputString);
238 }
239
240 @override
241 String dateHelpText(MaterialLocalizations localizations) {
242 return localizations.dateHelpText;
243 }
244}
245
246/// Utility functions for working with dates.
247abstract final class DateUtils {
248 /// {@template flutter.material.date.dateOnly}
249 /// Returns a [DateTime] with the date of the original, but time set to
250 /// midnight.
251 /// {@endtemplate}
252 static DateTime dateOnly(DateTime date) {
253 return DateTime(date.year, date.month, date.day);
254 }
255
256 /// {@template flutter.material.date.datesOnly}
257 /// Returns a [DateTimeRange] with the dates of the original, but with times
258 /// set to midnight.
259 ///
260 /// See also:
261 /// * [dateOnly], which does the same thing for a single date.
262 /// {@endtemplate}
263 static DateTimeRange datesOnly(DateTimeRange range) {
264 return DateTimeRange(start: dateOnly(range.start), end: dateOnly(range.end));
265 }
266
267 /// {@template flutter.material.date.isSameDay}
268 /// Returns true if the two [DateTime] objects have the same day, month, and
269 /// year, or are both null.
270 /// {@endtemplate}
271 static bool isSameDay(DateTime? dateA, DateTime? dateB) {
272 return dateA?.year == dateB?.year && dateA?.month == dateB?.month && dateA?.day == dateB?.day;
273 }
274
275 /// {@template flutter.material.date.isSameMonth}
276 /// Returns true if the two [DateTime] objects have the same month and
277 /// year, or are both null.
278 /// {@endtemplate}
279 static bool isSameMonth(DateTime? dateA, DateTime? dateB) {
280 return dateA?.year == dateB?.year && dateA?.month == dateB?.month;
281 }
282
283 /// {@template flutter.material.date.monthDelta}
284 /// Determines the number of months between two [DateTime] objects.
285 ///
286 /// For example:
287 ///
288 /// ```dart
289 /// DateTime date1 = DateTime(2019, 6, 15);
290 /// DateTime date2 = DateTime(2020, 1, 15);
291 /// int delta = DateUtils.monthDelta(date1, date2);
292 /// ```
293 ///
294 /// The value for `delta` would be `7`.
295 /// {@endtemplate}
296 static int monthDelta(DateTime startDate, DateTime endDate) {
297 return (endDate.year - startDate.year) * 12 + endDate.month - startDate.month;
298 }
299
300 /// {@template flutter.material.date.addMonthsToMonthDate}
301 /// Returns a [DateTime] that is [monthDate] with the added number
302 /// of months and the day set to 1 and time set to midnight.
303 ///
304 /// For example:
305 ///
306 /// ```dart
307 /// DateTime date = DateTime(2019, 1, 15);
308 /// DateTime futureDate = DateUtils.addMonthsToMonthDate(date, 3);
309 /// ```
310 ///
311 /// `date` would be January 15, 2019.
312 /// `futureDate` would be April 1, 2019 since it adds 3 months.
313 /// {@endtemplate}
314 static DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) {
315 return DateTime(monthDate.year, monthDate.month + monthsToAdd);
316 }
317
318 /// {@template flutter.material.date.addDaysToDate}
319 /// Returns a [DateTime] with the added number of days and time set to
320 /// midnight.
321 /// {@endtemplate}
322 static DateTime addDaysToDate(DateTime date, int days) {
323 return DateTime(date.year, date.month, date.day + days);
324 }
325
326 /// {@template flutter.material.date.firstDayOffset}
327 /// Computes the offset from the first day of the week that the first day of
328 /// the [month] falls on.
329 ///
330 /// For example, September 1, 2017 falls on a Friday, which in the calendar
331 /// localized for United States English appears as:
332 ///
333 /// S M T W T F S
334 /// _ _ _ _ _ 1 2
335 ///
336 /// The offset for the first day of the months is the number of leading blanks
337 /// in the calendar, i.e. 5.
338 ///
339 /// The same date localized for the Russian calendar has a different offset,
340 /// because the first day of week is Monday rather than Sunday:
341 ///
342 /// M T W T F S S
343 /// _ _ _ _ 1 2 3
344 ///
345 /// So the offset is 4, rather than 5.
346 ///
347 /// This code consolidates the following:
348 ///
349 /// - [DateTime.weekday] provides a 1-based index into days of week, with 1
350 /// falling on Monday.
351 /// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index
352 /// into the [MaterialLocalizations.narrowWeekdays] list.
353 /// - [MaterialLocalizations.narrowWeekdays] list provides localized names of
354 /// days of week, always starting with Sunday and ending with Saturday.
355 /// {@endtemplate}
356 static int firstDayOffset(int year, int month, MaterialLocalizations localizations) {
357 // 0-based day of week for the month and year, with 0 representing Monday.
358 final int weekdayFromMonday = DateTime(year, month).weekday - 1;
359
360 // 0-based start of week depending on the locale, with 0 representing Sunday.
361 int firstDayOfWeekIndex = localizations.firstDayOfWeekIndex;
362
363 // firstDayOfWeekIndex recomputed to be Monday-based, in order to compare with
364 // weekdayFromMonday.
365 firstDayOfWeekIndex = (firstDayOfWeekIndex - 1) % 7;
366
367 // Number of days between the first day of week appearing on the calendar,
368 // and the day corresponding to the first of the month.
369 return (weekdayFromMonday - firstDayOfWeekIndex) % 7;
370 }
371
372 /// {@template flutter.material.date.getDaysInMonth}
373 /// Returns the number of days in a month, according to the proleptic
374 /// Gregorian calendar.
375 ///
376 /// This applies the leap year logic introduced by the Gregorian reforms of
377 /// 1582. It will not give valid results for dates prior to that time.
378 /// {@endtemplate}
379 static int getDaysInMonth(int year, int month) {
380 if (month == DateTime.february) {
381 final bool isLeapYear = (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
382 return isLeapYear ? 29 : 28;
383 }
384 const List<int> daysInMonth = <int>[31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
385 return daysInMonth[month - 1];
386 }
387}
388
389/// Mode of date entry method for the date picker dialog.
390///
391/// In [calendar] mode, a calendar grid is displayed and the user taps the
392/// day they wish to select. In [input] mode, a [TextField] is displayed and
393/// the user types in the date they wish to select.
394///
395/// [calendarOnly] and [inputOnly] are variants of the above that don't
396/// allow the user to change to the mode.
397///
398/// See also:
399///
400/// * [showDatePicker] and [showDateRangePicker], which use this to control
401/// the initial entry mode of their dialogs.
402enum DatePickerEntryMode {
403 /// User picks a date from calendar grid. Can switch to [input] by activating
404 /// a mode button in the dialog.
405 calendar,
406
407 /// User can input the date by typing it into a text field.
408 ///
409 /// Can switch to [calendar] by activating a mode button in the dialog.
410 input,
411
412 /// User can only pick a date from calendar grid.
413 ///
414 /// There is no user interface to switch to another mode.
415 calendarOnly,
416
417 /// User can only input the date by typing it into a text field.
418 ///
419 /// There is no user interface to switch to another mode.
420 inputOnly,
421}
422
423/// Initial display of a calendar date picker.
424///
425/// Either a grid of available years or a monthly calendar.
426///
427/// See also:
428///
429/// * [showDatePicker], which shows a dialog that contains a Material Design
430/// date picker.
431/// * [CalendarDatePicker], widget which implements the Material Design date picker.
432enum DatePickerMode {
433 /// Choosing a month and day.
434 day,
435
436 /// Choosing a year.
437 year,
438}
439
440/// Signature for predicating dates for enabled date selections.
441///
442/// See [showDatePicker], which has a [SelectableDayPredicate] parameter used
443/// to specify allowable days in the date picker.
444typedef SelectableDayPredicate = bool Function(DateTime day);
445
446/// Encapsulates a start and end [DateTime] that represent the range of dates.
447///
448/// The range includes the [start] and [end] dates. The [start] and [end] dates
449/// may be equal to indicate a date range of a single day. The [start] date must
450/// not be after the [end] date.
451///
452/// See also:
453/// * [showDateRangePicker], which displays a dialog that allows the user to
454/// select a date range.
455@immutable
456@optionalTypeArgs
457class DateTimeRange<T extends DateTime> {
458 /// Creates a date range for the given start and end [DateTime].
459 DateTimeRange({required this.start, required this.end}) : assert(!start.isAfter(end));
460
461 /// The start of the range of dates.
462 final T start;
463
464 /// The end of the range of dates.
465 final T end;
466
467 /// Returns a [Duration] of the time between [start] and [end].
468 ///
469 /// See [DateTime.difference] for more details.
470 Duration get duration => end.difference(start);
471
472 @override
473 bool operator ==(Object other) {
474 if (other.runtimeType != runtimeType) {
475 return false;
476 }
477 return other is DateTimeRange && other.start == start && other.end == end;
478 }
479
480 @override
481 int get hashCode => Object.hash(start, end);
482
483 @override
484 String toString() => '$start - $end';
485}
486

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com