Unverified Commit 142b526f authored by Darren Austin's avatar Darren Austin Committed by GitHub

Material Date Picker redesign (#50546)

Date Picker UI redesign
parent 25ef78e2
...@@ -41,17 +41,23 @@ ConstructorGenerator generateMaterialConstructor = (LocaleInfo locale) { ...@@ -41,17 +41,23 @@ ConstructorGenerator generateMaterialConstructor = (LocaleInfo locale) {
const MaterialLocalization${locale.camelCase()}({ const MaterialLocalization${locale.camelCase()}({
String localeName = '$localeName', String localeName = '$localeName',
@required intl.DateFormat fullYearFormat, @required intl.DateFormat fullYearFormat,
@required intl.DateFormat compactDateFormat,
@required intl.DateFormat shortDateFormat,
@required intl.DateFormat mediumDateFormat, @required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat, @required intl.DateFormat longDateFormat,
@required intl.DateFormat yearMonthFormat, @required intl.DateFormat yearMonthFormat,
@required intl.DateFormat shortMonthDayFormat,
@required intl.NumberFormat decimalFormat, @required intl.NumberFormat decimalFormat,
@required intl.NumberFormat twoDigitZeroPaddedFormat, @required intl.NumberFormat twoDigitZeroPaddedFormat,
}) : super( }) : super(
localeName: localeName, localeName: localeName,
fullYearFormat: fullYearFormat, fullYearFormat: fullYearFormat,
compactDateFormat: compactDateFormat,
shortDateFormat: shortDateFormat,
mediumDateFormat: mediumDateFormat, mediumDateFormat: mediumDateFormat,
longDateFormat: longDateFormat, longDateFormat: longDateFormat,
yearMonthFormat: yearMonthFormat, yearMonthFormat: yearMonthFormat,
shortMonthDayFormat: shortMonthDayFormat,
decimalFormat: decimalFormat, decimalFormat: decimalFormat,
twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat,
);'''; );''';
...@@ -63,15 +69,18 @@ const String materialFactoryDeclaration = ''' ...@@ -63,15 +69,18 @@ const String materialFactoryDeclaration = '''
GlobalMaterialLocalizations getMaterialTranslation( GlobalMaterialLocalizations getMaterialTranslation(
Locale locale, Locale locale,
intl.DateFormat fullYearFormat, intl.DateFormat fullYearFormat,
intl.DateFormat compactDateFormat,
intl.DateFormat shortDateFormat,
intl.DateFormat mediumDateFormat, intl.DateFormat mediumDateFormat,
intl.DateFormat longDateFormat, intl.DateFormat longDateFormat,
intl.DateFormat yearMonthFormat, intl.DateFormat yearMonthFormat,
intl.DateFormat shortMonthDayFormat,
intl.NumberFormat decimalFormat, intl.NumberFormat decimalFormat,
intl.NumberFormat twoDigitZeroPaddedFormat, intl.NumberFormat twoDigitZeroPaddedFormat,
) {'''; ) {''';
const String materialFactoryArguments = const String materialFactoryArguments =
'fullYearFormat: fullYearFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat'; 'fullYearFormat: fullYearFormat, compactDateFormat: compactDateFormat, shortDateFormat: shortDateFormat, mediumDateFormat: mediumDateFormat, longDateFormat: longDateFormat, yearMonthFormat: yearMonthFormat, shortMonthDayFormat: shortMonthDayFormat, decimalFormat: decimalFormat, twoDigitZeroPaddedFormat: twoDigitZeroPaddedFormat';
const String materialSupportedLanguagesConstant = 'kMaterialSupportedLanguages'; const String materialSupportedLanguagesConstant = 'kMaterialSupportedLanguages';
......
...@@ -46,7 +46,6 @@ export 'src/material/colors.dart'; ...@@ -46,7 +46,6 @@ export 'src/material/colors.dart';
export 'src/material/constants.dart'; export 'src/material/constants.dart';
export 'src/material/data_table.dart'; export 'src/material/data_table.dart';
export 'src/material/data_table_source.dart'; export 'src/material/data_table_source.dart';
export 'src/material/date_picker.dart';
export 'src/material/debug.dart'; export 'src/material/debug.dart';
export 'src/material/dialog.dart'; export 'src/material/dialog.dart';
export 'src/material/dialog_theme.dart'; export 'src/material/dialog_theme.dart';
...@@ -86,6 +85,7 @@ export 'src/material/outline_button.dart'; ...@@ -86,6 +85,7 @@ export 'src/material/outline_button.dart';
export 'src/material/page.dart'; export 'src/material/page.dart';
export 'src/material/page_transitions_theme.dart'; export 'src/material/page_transitions_theme.dart';
export 'src/material/paginated_data_table.dart'; export 'src/material/paginated_data_table.dart';
export 'src/material/pickers/pickers.dart';
export 'src/material/popup_menu.dart'; export 'src/material/popup_menu.dart';
export 'src/material/popup_menu_theme.dart'; export 'src/material/popup_menu_theme.dart';
export 'src/material/progress_indicator.dart'; export 'src/material/progress_indicator.dart';
......
...@@ -224,6 +224,29 @@ abstract class MaterialLocalizations { ...@@ -224,6 +224,29 @@ abstract class MaterialLocalizations {
/// Full unabbreviated year format, e.g. 2017 rather than 17. /// Full unabbreviated year format, e.g. 2017 rather than 17.
String formatYear(DateTime date); String formatYear(DateTime date);
/// Formats the date in a compact format.
///
/// Usually just the numeric values for the for day, month and year are used.
///
/// Examples:
///
/// - US English: 02/21/2019
/// - Russian: 21.02.2019
///
/// See also:
/// * [parseCompactDate], which will convert a compact date string to a [DateTime].
String formatCompactDate(DateTime date);
/// Formats the date using a short-width format.
///
/// Includes the abbreviation of the month, the day and year.
///
/// Examples:
///
/// - US English: Feb 21, 2019
/// - Russian: 21 февр. 2019 г.
String formatShortDate(DateTime date);
/// Formats the date using a medium-width format. /// Formats the date using a medium-width format.
/// ///
/// Abbreviates month and days of week. This appears in the header of the date /// Abbreviates month and days of week. This appears in the header of the date
...@@ -252,6 +275,24 @@ abstract class MaterialLocalizations { ...@@ -252,6 +275,24 @@ abstract class MaterialLocalizations {
/// in the date picker invoked using [showDatePicker]. /// in the date picker invoked using [showDatePicker].
String formatMonthYear(DateTime date); String formatMonthYear(DateTime date);
/// Formats the month and day of the given [date].
///
/// Examples:
///
/// - US English: Feb 21
/// - Russian: 21 февр.
String formatShortMonthDay(DateTime date);
/// Converts the given compact date formatted string into a [DateTime].
///
/// The format of the string must be a valid compact date format for the
/// given locale. If the text doesn't represent a valid date, `null` will be
/// returned.
///
/// See also:
/// * [formatCompactDate], which will convert a [DateTime] into a string in the compact format.
DateTime parseCompactDate(String inputString);
/// List of week day names in narrow format, usually 1- or 2-letter /// List of week day names in narrow format, usually 1- or 2-letter
/// abbreviations of full names. /// abbreviations of full names.
/// ///
...@@ -437,6 +478,23 @@ class DefaultMaterialLocalizations implements MaterialLocalizations { ...@@ -437,6 +478,23 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
'December', 'December',
]; ];
/// Returns the number of days in a month, according to the proleptic
/// Gregorian calendar.
///
/// This applies the leap year logic introduced by the Gregorian reforms of
/// 1582. It will not give valid results for dates prior to that time.
int _getDaysInMonth(int year, int month) {
if (month == DateTime.february) {
final bool isLeapYear = (year % 4 == 0) && (year % 100 != 0) ||
(year % 400 == 0);
if (isLeapYear)
return 29;
return 28;
}
const List<int> daysInMonth = <int>[31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
return daysInMonth[month - 1];
}
@override @override
String formatHour(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat = false }) { String formatHour(TimeOfDay timeOfDay, { bool alwaysUse24HourFormat = false }) {
final TimeOfDayFormat format = timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat); final TimeOfDayFormat format = timeOfDayFormat(alwaysUse24HourFormat: alwaysUse24HourFormat);
...@@ -470,6 +528,21 @@ class DefaultMaterialLocalizations implements MaterialLocalizations { ...@@ -470,6 +528,21 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
@override @override
String formatYear(DateTime date) => date.year.toString(); String formatYear(DateTime date) => date.year.toString();
@override
String formatCompactDate(DateTime date) {
// Assumes US mm/dd/yyyy format
final String month = _formatTwoDigitZeroPad(date.month);
final String day = _formatTwoDigitZeroPad(date.day);
final String year = date.year.toString().padLeft(4, '0');
return '$month/$day/$year';
}
@override
String formatShortDate(DateTime date) {
final String month = _shortMonths[date.month - DateTime.january];
return '$month ${date.day}, ${date.year}';
}
@override @override
String formatMediumDate(DateTime date) { String formatMediumDate(DateTime date) {
final String day = _shortWeekdays[date.weekday - DateTime.monday]; final String day = _shortWeekdays[date.weekday - DateTime.monday];
...@@ -490,6 +563,37 @@ class DefaultMaterialLocalizations implements MaterialLocalizations { ...@@ -490,6 +563,37 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
return '$month $year'; return '$month $year';
} }
@override
String formatShortMonthDay(DateTime date) {
final String month = _shortMonths[date.month - DateTime.january];
return '$month ${date.day}';
}
@override
DateTime parseCompactDate(String inputString) {
// Assumes US mm/dd/yyyy format
final List<String> inputParts = inputString.split('/');
if (inputParts.length != 3) {
return null;
}
final int year = int.tryParse(inputParts[2], radix: 10);
if (year == null || year < 1) {
return null;
}
final int month = int.tryParse(inputParts[0], radix: 10);
if (month == null || month < 1 || month > 12) {
return null;
}
final int day = int.tryParse(inputParts[1], radix: 10);
if (day == null || day < 1 || day > _getDaysInMonth(year, month)) {
return null;
}
return DateTime(year, month, day);
}
@override @override
List<String> get narrowWeekdays => _narrowWeekdays; List<String> get narrowWeekdays => _narrowWeekdays;
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Mode of the date picker dialog.
///
/// Either a calendar or text input. In [calendar] mode, a calendar view is
/// displayed and the user taps the day they wish to select. In [input] mode a
/// [TextField] is displayed and the user types in the date they wish to select.
enum DatePickerEntryMode {
/// Tapping on a calendar.
calendar,
/// Text input.
input,
}
/// Initial display of a calendar date picker.
///
/// Either a grid of available years or a monthly calendar.
///
/// See also:
///
/// * [showDatePicker], which shows a dialog that contains a material design
/// date picker.
/// * [CalendarDatePicker], widget which implements the material design date picker.
enum DatePickerMode {
/// Choosing a month and day.
day,
/// Choosing a year.
year,
}
/// Signature for predicating dates for enabled date selections.
///
/// See [showDatePicker].
typedef SelectableDayPredicate = bool Function(DateTime day);
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import '../color_scheme.dart';
import '../icon_button.dart';
import '../text_theme.dart';
import '../theme.dart';
// NOTE: This is an internal implementation file. Even though there are public
// classes and functions defined here, they are only meant to be used by the
// date picker implementation and are not exported as part of the Material library.
// See pickers.dart for exactly what is considered part of the public API.
const double _datePickerHeaderLandscapeWidth = 152.0;
const double _datePickerHeaderPortraitHeight = 120.0;
const double _headerPaddingLandscape = 16.0;
/// Re-usable widget that displays the selected date (in large font) and the
/// help text above it.
///
/// These types include:
///
/// * Single Date picker with calendar mode.
/// * Single Date picker with manual input mode.
///
/// [helpText], [orientation], [icon], [onIconPressed] are required and must be
/// non-null.
class DatePickerHeader extends StatelessWidget {
/// Creates a header for use in a date picker dialog.
const DatePickerHeader({
Key key,
@required this.helpText,
@required this.titleText,
this.titleSemanticsLabel,
@required this.titleStyle,
@required this.orientation,
this.isShort = false,
@required this.icon,
@required this.iconTooltip,
@required this.onIconPressed,
}) : assert(helpText != null),
assert(orientation != null),
assert(isShort != null),
super(key: key);
/// The text that is displayed at the top of the header.
///
/// This is used to indicate to the user what they are selecting a date for.
final String helpText;
/// The text that is displayed at the center of the header.
final String titleText;
/// The semantic label associated with the [titleText].
final String titleSemanticsLabel;
/// The [TextStyle] that the title text is displayed with.
final TextStyle titleStyle;
/// The orientation is used to decide how to layout its children.
final Orientation orientation;
/// Indicates the header is being displayed in a shorter/narrower context.
///
/// This will be used to tighten up the space between the help text and date
/// text if `true`. Additionally, it will use a smaller typography style if
/// `true`.
///
/// This is necessary for displaying the manual input mode in
/// landscape orientation, in order to account for the keyboard height.
final bool isShort;
/// The mode-switching icon that will be displayed in the lower right
/// in portrait, and lower left in landscape.
///
/// The available icons are described in [Icons].
final IconData icon;
/// The text that is displayed for the tooltip of the icon.
final String iconTooltip;
/// Callback when the user taps the icon in the header.
///
/// The picker will use this to toggle between entry modes.
final VoidCallback onIconPressed;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
final TextTheme textTheme = theme.textTheme;
// The header should use the primary color in light themes and surface color in dark
final bool isDark = colorScheme.brightness == Brightness.dark;
final Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary;
final Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary;
final TextStyle helpStyle = textTheme.overline?.copyWith(
color: onPrimarySurfaceColor,
);
final Text help = Text(
helpText,
style: helpStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
final Text title = Text(
titleText,
semanticsLabel: titleSemanticsLabel ?? titleText,
style: titleStyle,
maxLines: (isShort || orientation == Orientation.portrait) ? 1 : 2,
overflow: TextOverflow.ellipsis,
);
final IconButton icon = IconButton(
icon: Icon(this.icon),
color: onPrimarySurfaceColor,
tooltip: iconTooltip,
onPressed: onIconPressed,
);
switch (orientation) {
case Orientation.portrait:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: _datePickerHeaderPortraitHeight,
color: primarySurfaceColor,
padding: const EdgeInsetsDirectional.only(
start: 24,
end: 12,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 16),
Flexible(child: help),
const SizedBox(height: 38),
Row(
children: <Widget>[
Expanded(child: title),
icon,
],
),
],
),
),
],
);
case Orientation.landscape:
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: _datePickerHeaderLandscapeWidth,
color: primarySurfaceColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: _headerPaddingLandscape,
),
child: help,
),
SizedBox(height: isShort ? 16 : 56),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: _headerPaddingLandscape,
),
child: title,
),
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4,
),
child: icon,
),
],
),
),
],
);
}
return null;
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Common date utility functions used by the date picker implementation
// NOTE: This is an internal implementation file. Even though there are public
// classes and functions defined here, they are only meant to be used by the
// date picker implementation and are not exported as part of the Material library.
// See pickers.dart for exactly what is considered part of the public API.
import '../material_localizations.dart';
/// Returns a [DateTime] with just the date of the original, but no time set.
DateTime dateOnly(DateTime date) {
return DateTime(date.year, date.month, date.day);
}
/// Returns true if the two [DateTime] objects have the same day, month, and
/// year.
bool isSameDay(DateTime dateA, DateTime dateB) {
return
dateA.year == dateB.year &&
dateA.month == dateB.month &&
dateA.day == dateB.day;
}
/// Determines the number of months between two [DateTime] objects.
///
/// For example:
/// ```
/// DateTime date1 = DateTime(year: 2019, month: 6, day: 15);
/// DateTime date2 = DateTime(year: 2020, month: 1, day: 15);
/// int delta = monthDelta(date1, date2);
/// ```
///
/// The value for `delta` would be `7`.
int monthDelta(DateTime startDate, DateTime endDate) {
return (endDate.year - startDate.year) * 12 + endDate.month - startDate.month;
}
/// Returns a [DateTime] with the added number of months and truncates any day
/// and time information.
///
/// For example:
/// ```
/// DateTime date = DateTime(year: 2019, month: 1, day: 15);
/// DateTime futureDate = _addMonthsToMonthDate(date, 3);
/// ```
///
/// `date` would be January 15, 2019.
/// `futureDate` would be April 1, 2019 since it adds 3 months and truncates
/// any additional date information.
DateTime addMonthsToMonthDate(DateTime monthDate, int monthsToAdd) {
return DateTime(monthDate.year, monthDate.month + monthsToAdd);
}
/// Computes the offset from the first day of the week that the first day of
/// the [month] falls on.
///
/// For example, September 1, 2017 falls on a Friday, which in the calendar
/// localized for United States English appears as:
///
/// ```
/// S M T W T F S
/// _ _ _ _ _ 1 2
/// ```
///
/// The offset for the first day of the months is the number of leading blanks
/// in the calendar, i.e. 5.
///
/// The same date localized for the Russian calendar has a different offset,
/// because the first day of week is Monday rather than Sunday:
///
/// ```
/// M T W T F S S
/// _ _ _ _ 1 2 3
/// ```
///
/// So the offset is 4, rather than 5.
///
/// This code consolidates the following:
///
/// - [DateTime.weekday] provides a 1-based index into days of week, with 1
/// falling on Monday.
/// - [MaterialLocalizations.firstDayOfWeekIndex] provides a 0-based index
/// into the [MaterialLocalizations.narrowWeekdays] list.
/// - [MaterialLocalizations.narrowWeekdays] list provides localized names of
/// days of week, always starting with Sunday and ending with Saturday.
int firstDayOffset(int year, int month, MaterialLocalizations localizations) {
// 0-based day of week for the month and year, with 0 representing Monday.
final int weekdayFromMonday = DateTime(year, month).weekday - 1;
// 0-based start of week depending on the locale, with 0 representing Sunday.
int firstDayOfWeekIndex = localizations.firstDayOfWeekIndex;
// firstDayOfWeekIndex recomputed to be Monday-based, in order to compare with
// weekdayFromMonday.
firstDayOfWeekIndex = (firstDayOfWeekIndex - 1) % 7;
// Number of days between the first day of week appearing on the calendar,
// and the day corresponding to the first of the month.
return (weekdayFromMonday - firstDayOfWeekIndex) % 7;
}
/// Returns the number of days in a month, according to the proleptic
/// Gregorian calendar.
///
/// This applies the leap year logic introduced by the Gregorian reforms of
/// 1582. It will not give valid results for dates prior to that time.
int getDaysInMonth(int year, int month) {
if (month == DateTime.february) {
final bool isLeapYear = (year % 4 == 0) && (year % 100 != 0) ||
(year % 400 == 0);
if (isLeapYear)
return 29;
return 28;
}
const List<int> daysInMonth = <int>[31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
return daysInMonth[month - 1];
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Date Picker public API
export 'calendar_date_picker.dart' show CalendarDatePicker;
export 'date_picker_common.dart' show DatePickerEntryMode, DatePickerMode, SelectableDayPredicate;
export 'date_picker_deprecated.dart';
export 'date_picker_dialog.dart' show showDatePicker;
export 'input_date_picker.dart' show InputDatePickerFormField;
...@@ -76,12 +76,14 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations { ...@@ -76,12 +76,14 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
/// 1. The string that would be returned by [Intl.canonicalizedLocale] for /// 1. The string that would be returned by [Intl.canonicalizedLocale] for
/// the locale. /// the locale.
/// 2. The [intl.DateFormat] for [formatYear]. /// 2. The [intl.DateFormat] for [formatYear].
/// 3. The [intl.DateFormat] for [formatMediumDate]. /// 3. The [int.DateFormat] for [formatShortDate].
/// 4. The [intl.DateFormat] for [formatFullDate]. /// 4. The [intl.DateFormat] for [formatMediumDate].
/// 5. The [intl.DateFormat] for [formatMonthYear]. /// 5. The [intl.DateFormat] for [formatFullDate].
/// 6. The [NumberFormat] for [formatDecimal] (also used by [formatHour] and /// 6. The [intl.DateFormat] for [formatMonthYear].
/// 7. The [int.DateFormat] for [formatShortMonthDay].
/// 8. The [NumberFormat] for [formatDecimal] (also used by [formatHour] and
/// [formatTimeOfDay] when [timeOfDayFormat] doesn't use [HourFormat.HH]). /// [formatTimeOfDay] when [timeOfDayFormat] doesn't use [HourFormat.HH]).
/// 7. The [NumberFormat] for [formatHour] and the hour part of /// 9. The [NumberFormat] for [formatHour] and the hour part of
/// [formatTimeOfDay] when [timeOfDayFormat] uses [HourFormat.HH], and for /// [formatTimeOfDay] when [timeOfDayFormat] uses [HourFormat.HH], and for
/// [formatMinute] and the minute part of [formatTimeOfDay]. /// [formatMinute] and the minute part of [formatTimeOfDay].
/// ///
...@@ -90,21 +92,30 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations { ...@@ -90,21 +92,30 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
const GlobalMaterialLocalizations({ const GlobalMaterialLocalizations({
@required String localeName, @required String localeName,
@required intl.DateFormat fullYearFormat, @required intl.DateFormat fullYearFormat,
@required intl.DateFormat compactDateFormat,
@required intl.DateFormat shortDateFormat,
@required intl.DateFormat mediumDateFormat, @required intl.DateFormat mediumDateFormat,
@required intl.DateFormat longDateFormat, @required intl.DateFormat longDateFormat,
@required intl.DateFormat yearMonthFormat, @required intl.DateFormat yearMonthFormat,
@required intl.DateFormat shortMonthDayFormat,
@required intl.NumberFormat decimalFormat, @required intl.NumberFormat decimalFormat,
@required intl.NumberFormat twoDigitZeroPaddedFormat, @required intl.NumberFormat twoDigitZeroPaddedFormat,
}) : assert(localeName != null), }) : assert(localeName != null),
_localeName = localeName, _localeName = localeName,
assert(fullYearFormat != null), assert(fullYearFormat != null),
_fullYearFormat = fullYearFormat, _fullYearFormat = fullYearFormat,
assert(compactDateFormat != null),
_compactDateFormat = compactDateFormat,
assert(shortDateFormat != null),
_shortDateFormat = shortDateFormat,
assert(mediumDateFormat != null), assert(mediumDateFormat != null),
_mediumDateFormat = mediumDateFormat, _mediumDateFormat = mediumDateFormat,
assert(longDateFormat != null), assert(longDateFormat != null),
_longDateFormat = longDateFormat, _longDateFormat = longDateFormat,
assert(yearMonthFormat != null), assert(yearMonthFormat != null),
_yearMonthFormat = yearMonthFormat, _yearMonthFormat = yearMonthFormat,
assert(shortMonthDayFormat != null),
_shortMonthDayFormat = shortMonthDayFormat,
assert(decimalFormat != null), assert(decimalFormat != null),
_decimalFormat = decimalFormat, _decimalFormat = decimalFormat,
assert(twoDigitZeroPaddedFormat != null), assert(twoDigitZeroPaddedFormat != null),
...@@ -112,9 +123,12 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations { ...@@ -112,9 +123,12 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
final String _localeName; final String _localeName;
final intl.DateFormat _fullYearFormat; final intl.DateFormat _fullYearFormat;
final intl.DateFormat _compactDateFormat;
final intl.DateFormat _shortDateFormat;
final intl.DateFormat _mediumDateFormat; final intl.DateFormat _mediumDateFormat;
final intl.DateFormat _longDateFormat; final intl.DateFormat _longDateFormat;
final intl.DateFormat _yearMonthFormat; final intl.DateFormat _yearMonthFormat;
final intl.DateFormat _shortMonthDayFormat;
final intl.NumberFormat _decimalFormat; final intl.NumberFormat _decimalFormat;
final intl.NumberFormat _twoDigitZeroPaddedFormat; final intl.NumberFormat _twoDigitZeroPaddedFormat;
...@@ -142,6 +156,16 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations { ...@@ -142,6 +156,16 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
return _fullYearFormat.format(date); return _fullYearFormat.format(date);
} }
@override
String formatCompactDate(DateTime date) {
return _compactDateFormat.format(date);
}
@override
String formatShortDate(DateTime date) {
return _shortDateFormat.format(date);
}
@override @override
String formatMediumDate(DateTime date) { String formatMediumDate(DateTime date) {
return _mediumDateFormat.format(date); return _mediumDateFormat.format(date);
...@@ -157,6 +181,20 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations { ...@@ -157,6 +181,20 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
return _yearMonthFormat.format(date); return _yearMonthFormat.format(date);
} }
@override
String formatShortMonthDay(DateTime date) {
return _shortMonthDayFormat.format(date);
}
@override
DateTime parseCompactDate(String inputString) {
try {
return _compactDateFormat.parseStrict(inputString);
} on FormatException {
return null;
}
}
@override @override
List<String> get narrowWeekdays { List<String> get narrowWeekdays {
return _longDateFormat.dateSymbols.NARROWWEEKDAYS; return _longDateFormat.dateSymbols.NARROWWEEKDAYS;
...@@ -576,24 +614,36 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal ...@@ -576,24 +614,36 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal
); );
intl.DateFormat fullYearFormat; intl.DateFormat fullYearFormat;
intl.DateFormat compactDateFormat;
intl.DateFormat shortDateFormat;
intl.DateFormat mediumDateFormat; intl.DateFormat mediumDateFormat;
intl.DateFormat longDateFormat; intl.DateFormat longDateFormat;
intl.DateFormat yearMonthFormat; intl.DateFormat yearMonthFormat;
intl.DateFormat shortMonthDayFormat;
if (intl.DateFormat.localeExists(localeName)) { if (intl.DateFormat.localeExists(localeName)) {
fullYearFormat = intl.DateFormat.y(localeName); fullYearFormat = intl.DateFormat.y(localeName);
compactDateFormat = intl.DateFormat.yMd(localeName);
shortDateFormat = intl.DateFormat.yMMMd(localeName);
mediumDateFormat = intl.DateFormat.MMMEd(localeName); mediumDateFormat = intl.DateFormat.MMMEd(localeName);
longDateFormat = intl.DateFormat.yMMMMEEEEd(localeName); longDateFormat = intl.DateFormat.yMMMMEEEEd(localeName);
yearMonthFormat = intl.DateFormat.yMMMM(localeName); yearMonthFormat = intl.DateFormat.yMMMM(localeName);
shortMonthDayFormat = intl.DateFormat.MMMd(localeName);
} else if (intl.DateFormat.localeExists(locale.languageCode)) { } else if (intl.DateFormat.localeExists(locale.languageCode)) {
fullYearFormat = intl.DateFormat.y(locale.languageCode); fullYearFormat = intl.DateFormat.y(locale.languageCode);
compactDateFormat = intl.DateFormat.yMd(locale.languageCode);
shortDateFormat = intl.DateFormat.yMMMd(locale.languageCode);
mediumDateFormat = intl.DateFormat.MMMEd(locale.languageCode); mediumDateFormat = intl.DateFormat.MMMEd(locale.languageCode);
longDateFormat = intl.DateFormat.yMMMMEEEEd(locale.languageCode); longDateFormat = intl.DateFormat.yMMMMEEEEd(locale.languageCode);
yearMonthFormat = intl.DateFormat.yMMMM(locale.languageCode); yearMonthFormat = intl.DateFormat.yMMMM(locale.languageCode);
shortMonthDayFormat = intl.DateFormat.MMMd(locale.languageCode);
} else { } else {
fullYearFormat = intl.DateFormat.y(); fullYearFormat = intl.DateFormat.y();
compactDateFormat = intl.DateFormat.yMd();
shortDateFormat = intl.DateFormat.yMMMd();
mediumDateFormat = intl.DateFormat.MMMEd(); mediumDateFormat = intl.DateFormat.MMMEd();
longDateFormat = intl.DateFormat.yMMMMEEEEd(); longDateFormat = intl.DateFormat.yMMMMEEEEd();
yearMonthFormat = intl.DateFormat.yMMMM(); yearMonthFormat = intl.DateFormat.yMMMM();
shortMonthDayFormat = intl.DateFormat.MMMd();
} }
intl.NumberFormat decimalFormat; intl.NumberFormat decimalFormat;
...@@ -612,9 +662,12 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal ...@@ -612,9 +662,12 @@ class _MaterialLocalizationsDelegate extends LocalizationsDelegate<MaterialLocal
return SynchronousFuture<MaterialLocalizations>(getMaterialTranslation( return SynchronousFuture<MaterialLocalizations>(getMaterialTranslation(
locale, locale,
fullYearFormat, fullYearFormat,
compactDateFormat,
shortDateFormat,
mediumDateFormat, mediumDateFormat,
longDateFormat, longDateFormat,
yearMonthFormat, yearMonthFormat,
shortMonthDayFormat,
decimalFormat, decimalFormat,
twoDigitZeroPaddedFormat, twoDigitZeroPaddedFormat,
)); ));
......
...@@ -19,7 +19,7 @@ void main() { ...@@ -19,7 +19,7 @@ void main() {
initialDate = DateTime(2016, DateTime.january, 15); initialDate = DateTime(2016, DateTime.january, 15);
}); });
group(DayPicker, () { group(CalendarDatePicker, () {
final intl.NumberFormat arabicNumbers = intl.NumberFormat('0', 'ar'); final intl.NumberFormat arabicNumbers = intl.NumberFormat('0', 'ar');
final Map<Locale, Map<String, dynamic>> testLocales = <Locale, Map<String, dynamic>>{ final Map<Locale, Map<String, dynamic>> testLocales = <Locale, Map<String, dynamic>>{
// Tests the default. // Tests the default.
...@@ -59,13 +59,11 @@ void main() { ...@@ -59,13 +59,11 @@ void main() {
final TextDirection textDirection = testLocales[locale]['textDirection'] as TextDirection; final TextDirection textDirection = testLocales[locale]['textDirection'] as TextDirection;
final DateTime baseDate = DateTime(2017, 9, 27); final DateTime baseDate = DateTime(2017, 9, 27);
await _pumpBoilerplate(tester, DayPicker( await _pumpBoilerplate(tester, CalendarDatePicker(
selectedDate: baseDate, initialDate: baseDate,
currentDate: baseDate,
onChanged: (DateTime newValue) { },
firstDate: baseDate.subtract(const Duration(days: 90)), firstDate: baseDate.subtract(const Duration(days: 90)),
lastDate: baseDate.add(const Duration(days: 90)), lastDate: baseDate.add(const Duration(days: 90)),
displayedMonth: baseDate, onDateChanged: (DateTime newValue) {},
), locale: locale, textDirection: textDirection); ), locale: locale, textDirection: textDirection);
expect(find.text(expectedMonthYearHeader), findsOneWidget); expect(find.text(expectedMonthYearHeader), findsOneWidget);
...@@ -126,14 +124,14 @@ void main() { ...@@ -126,14 +124,14 @@ void main() {
await tester.tap(find.text('X')); await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1)); await tester.pumpAndSettle(const Duration(seconds: 1));
final Element dayPicker = tester.element(find.byType(DayPicker)); final Element picker = tester.element(find.byType(CalendarDatePicker));
expect( expect(
Localizations.localeOf(dayPicker), Localizations.localeOf(picker),
const Locale('fr', 'CA'), const Locale('fr', 'CA'),
); );
expect( expect(
Directionality.of(dayPicker), Directionality.of(picker),
TextDirection.ltr, TextDirection.ltr,
); );
...@@ -169,9 +167,9 @@ void main() { ...@@ -169,9 +167,9 @@ void main() {
await tester.tap(find.text('X')); await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1)); await tester.pumpAndSettle(const Duration(seconds: 1));
final Element dayPicker = tester.element(find.byType(DayPicker)); final Element picker = tester.element(find.byType(CalendarDatePicker));
expect( expect(
Directionality.of(dayPicker), Directionality.of(picker),
TextDirection.rtl, TextDirection.rtl,
); );
...@@ -210,14 +208,14 @@ void main() { ...@@ -210,14 +208,14 @@ void main() {
await tester.tap(find.text('X')); await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1)); await tester.pumpAndSettle(const Duration(seconds: 1));
final Element dayPicker = tester.element(find.byType(DayPicker)); final Element picker = tester.element(find.byType(CalendarDatePicker));
expect( expect(
Localizations.localeOf(dayPicker), Localizations.localeOf(picker),
const Locale('fr', 'CA'), const Locale('fr', 'CA'),
); );
expect( expect(
Directionality.of(dayPicker), Directionality.of(picker),
TextDirection.rtl, TextDirection.rtl,
); );
...@@ -292,12 +290,16 @@ Future<void> _pumpBoilerplate( ...@@ -292,12 +290,16 @@ Future<void> _pumpBoilerplate(
Locale locale = const Locale('en', 'US'), Locale locale = const Locale('en', 'US'),
TextDirection textDirection = TextDirection.ltr, TextDirection textDirection = TextDirection.ltr,
}) async { }) async {
await tester.pumpWidget(Directionality( await tester.pumpWidget(MaterialApp(
textDirection: TextDirection.ltr, home: Directionality(
child: Localizations( textDirection: TextDirection.ltr,
locale: locale, child: Localizations(
delegates: GlobalMaterialLocalizations.delegates, locale: locale,
child: child, delegates: GlobalMaterialLocalizations.delegates,
child: Material(
child: child,
),
),
), ),
)); ));
} }
...@@ -15,9 +15,12 @@ class FooMaterialLocalizations extends MaterialLocalizationEn { ...@@ -15,9 +15,12 @@ class FooMaterialLocalizations extends MaterialLocalizationEn {
) : super( ) : super(
localeName: localeName.toString(), localeName: localeName.toString(),
fullYearFormat: intl.DateFormat.y(), fullYearFormat: intl.DateFormat.y(),
compactDateFormat: intl.DateFormat.yMd(),
shortDateFormat: intl.DateFormat.yMMMd(),
mediumDateFormat: intl.DateFormat('E, MMM\u00a0d'), mediumDateFormat: intl.DateFormat('E, MMM\u00a0d'),
longDateFormat: intl.DateFormat.yMMMMEEEEd(), longDateFormat: intl.DateFormat.yMMMMEEEEd(),
yearMonthFormat: intl.DateFormat.yMMMM(), yearMonthFormat: intl.DateFormat.yMMMM(),
shortMonthDayFormat: intl.DateFormat.MMMd(),
decimalFormat: intl.NumberFormat.decimalPattern(), decimalFormat: intl.NumberFormat.decimalPattern(),
twoDigitZeroPaddedFormat: intl.NumberFormat('00'), twoDigitZeroPaddedFormat: intl.NumberFormat('00'),
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment