Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
2a8e35cc
Commit
2a8e35cc
authored
Sep 29, 2018
by
Viet Do
Committed by
xster
Sep 29, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cupertino Date Picker (#21251)
parent
efc5123d
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1474 additions
and
99 deletions
+1474
-99
cupertino_picker_demo.dart
...ter_gallery/lib/demo/cupertino/cupertino_picker_demo.dart
+154
-52
date_picker.dart
packages/flutter/lib/src/cupertino/date_picker.dart
+893
-14
localizations.dart
packages/flutter/lib/src/cupertino/localizations.dart
+64
-3
date_picker_test.dart
packages/flutter/test/cupertino/date_picker_test.dart
+360
-30
localizations_test.dart
packages/flutter/test/cupertino/localizations_test.dart
+3
-0
No files found.
examples/flutter_gallery/lib/demo/cupertino/cupertino_picker_demo.dart
View file @
2a8e35cc
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:intl/intl.dart'
;
import
'../../gallery/demo.dart'
;
import
'../../gallery/demo.dart'
;
import
'cupertino_navigation_demo.dart'
show
coolColorNames
;
import
'cupertino_navigation_demo.dart'
show
coolColorNames
;
...
@@ -22,6 +23,15 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
...
@@ -22,6 +23,15 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
Duration
timer
=
Duration
();
Duration
timer
=
Duration
();
// Value that is shown in the date picker in date mode.
DateTime
date
=
DateTime
.
now
();
// Value that is shown in the date picker in time mode.
DateTime
time
=
DateTime
.
now
();
// Value that is shown in the date picker in dateAndTime mode.
DateTime
dateTime
=
DateTime
.
now
();
Widget
_buildMenu
(
List
<
Widget
>
children
)
{
Widget
_buildMenu
(
List
<
Widget
>
children
)
{
return
Container
(
return
Container
(
decoration:
const
BoxDecoration
(
decoration:
const
BoxDecoration
(
...
@@ -53,30 +63,10 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
...
@@ -53,30 +63,10 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
);
);
}
}
Widget
_buildColorPicker
()
{
final
FixedExtentScrollController
scrollController
=
FixedExtentScrollController
(
initialItem:
_selectedColorIndex
);
return
CupertinoPicker
(
scrollController:
scrollController
,
itemExtent:
_kPickerItemHeight
,
backgroundColor:
CupertinoColors
.
white
,
onSelectedItemChanged:
(
int
index
)
{
setState
(()
{
_selectedColorIndex
=
index
;
});
},
children:
List
<
Widget
>.
generate
(
coolColorNames
.
length
,
(
int
index
)
{
return
Center
(
child:
Text
(
coolColorNames
[
index
]),
);
}),
);
}
Widget
_buildBottomPicker
(
Widget
picker
)
{
Widget
_buildBottomPicker
(
Widget
picker
)
{
return
Container
(
return
Container
(
height:
_kPickerSheetHeight
,
height:
_kPickerSheetHeight
,
padding:
const
EdgeInsets
.
only
(
top:
8
.0
),
padding:
const
EdgeInsets
.
only
(
top:
6
.0
),
color:
CupertinoColors
.
white
,
color:
CupertinoColors
.
white
,
child:
DefaultTextStyle
(
child:
DefaultTextStyle
(
style:
const
TextStyle
(
style:
const
TextStyle
(
...
@@ -95,6 +85,47 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
...
@@ -95,6 +85,47 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
);
);
}
}
Widget
_buildColorPicker
(
BuildContext
context
)
{
final
FixedExtentScrollController
scrollController
=
FixedExtentScrollController
(
initialItem:
_selectedColorIndex
);
return
GestureDetector
(
onTap:
()
async
{
await
showCupertinoModalPopup
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
{
return
_buildBottomPicker
(
CupertinoPicker
(
scrollController:
scrollController
,
itemExtent:
_kPickerItemHeight
,
backgroundColor:
CupertinoColors
.
white
,
onSelectedItemChanged:
(
int
index
)
{
setState
(()
=>
_selectedColorIndex
=
index
);
},
children:
List
<
Widget
>.
generate
(
coolColorNames
.
length
,
(
int
index
)
{
return
Center
(
child:
Text
(
coolColorNames
[
index
]),
);
}),
),
);
},
);
},
child:
_buildMenu
(
<
Widget
>[
const
Text
(
'Favorite Color'
),
Text
(
coolColorNames
[
_selectedColorIndex
],
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
),
],
),
);
}
Widget
_buildCountdownTimerPicker
(
BuildContext
context
)
{
Widget
_buildCountdownTimerPicker
(
BuildContext
context
)
{
return
GestureDetector
(
return
GestureDetector
(
onTap:
()
{
onTap:
()
{
...
@@ -105,9 +136,7 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
...
@@ -105,9 +136,7 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
CupertinoTimerPicker
(
CupertinoTimerPicker
(
initialTimerDuration:
timer
,
initialTimerDuration:
timer
,
onTimerDurationChanged:
(
Duration
newTimer
)
{
onTimerDurationChanged:
(
Duration
newTimer
)
{
setState
(()
{
setState
(()
=>
timer
=
newTimer
);
timer
=
newTimer
;
});
},
},
),
),
);
);
...
@@ -115,15 +144,105 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
...
@@ -115,15 +144,105 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
);
);
},
},
child:
_buildMenu
(
child:
_buildMenu
(
<
Widget
>[
<
Widget
>[
const
Text
(
'Countdown Timer'
),
const
Text
(
'Countdown Timer'
),
Text
(
Text
(
'
${timer.inHours}
:'
'
${timer.inHours}
:'
'
${(timer.inMinutes % 60).toString().padLeft(2,'0')}
:'
'
${(timer.inMinutes % 60).toString().padLeft(2,'0')}
:'
'
${(timer.inSeconds % 60).toString().padLeft(2,'0')}
'
,
'
${(timer.inSeconds % 60).toString().padLeft(2,'0')}
'
,
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
),
),
]
],
),
);
}
Widget
_buildDatePicker
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
showCupertinoModalPopup
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
{
return
_buildBottomPicker
(
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
date
,
initialDateTime:
date
,
onDateTimeChanged:
(
DateTime
newDateTime
)
{
setState
(()
=>
date
=
newDateTime
);
},
),
);
},
);
},
child:
_buildMenu
(
<
Widget
>[
const
Text
(
'Date'
),
Text
(
DateFormat
.
yMMMMd
().
format
(
date
),
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
),
]
),
);
}
Widget
_buildTimePicker
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
showCupertinoModalPopup
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
{
return
_buildBottomPicker
(
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
time
,
initialDateTime:
time
,
onDateTimeChanged:
(
DateTime
newDateTime
)
{
setState
(()
=>
time
=
newDateTime
);
},
),
);
},
);
},
child:
_buildMenu
(
<
Widget
>[
const
Text
(
'Time'
),
Text
(
DateFormat
.
jm
().
format
(
time
),
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
),
],
),
);
}
Widget
_buildDateAndTimePicker
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
showCupertinoModalPopup
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
{
return
_buildBottomPicker
(
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
dateAndTime
,
initialDateTime:
dateTime
,
onDateTimeChanged:
(
DateTime
newDateTime
)
{
setState
(()
=>
dateTime
=
newDateTime
);
},
),
);
},
);
},
child:
_buildMenu
(
<
Widget
>[
const
Text
(
'Date and Time'
),
Text
(
DateFormat
.
yMMMd
().
add_jm
().
format
(
dateTime
),
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
),
],
),
),
);
);
}
}
...
@@ -146,28 +265,11 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
...
@@ -146,28 +265,11 @@ class _CupertinoPickerDemoState extends State<CupertinoPickerDemo> {
child:
ListView
(
child:
ListView
(
children:
<
Widget
>[
children:
<
Widget
>[
const
Padding
(
padding:
EdgeInsets
.
only
(
top:
32.0
)),
const
Padding
(
padding:
EdgeInsets
.
only
(
top:
32.0
)),
GestureDetector
(
_buildColorPicker
(
context
),
onTap:
()
async
{
await
showCupertinoModalPopup
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
{
return
_buildBottomPicker
(
_buildColorPicker
());
},
);
},
child:
_buildMenu
(
<
Widget
>[
const
Text
(
'Favorite Color'
),
Text
(
coolColorNames
[
_selectedColorIndex
],
style:
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
),
),
]
),
),
_buildCountdownTimerPicker
(
context
),
_buildCountdownTimerPicker
(
context
),
_buildDatePicker
(
context
),
_buildTimePicker
(
context
),
_buildDateAndTimePicker
(
context
),
],
],
),
),
),
),
...
...
packages/flutter/lib/src/cupertino/date_picker.dart
View file @
2a8e35cc
...
@@ -2,30 +2,906 @@
...
@@ -2,30 +2,906 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
import
'localizations.dart'
;
import
'localizations.dart'
;
import
'picker.dart'
;
import
'picker.dart'
;
//
/
Default aesthetic values obtained by comparing with iOS pickers.
// Default aesthetic values obtained by comparing with iOS pickers.
const
double
_kItemExtent
=
32.0
;
const
double
_kItemExtent
=
32.0
;
const
double
_kPickerWidth
=
330.0
;
const
double
_kPickerWidth
=
330.0
;
/// Considers setting the default background color from the theme, in the future.
const
bool
_kUseMagnifier
=
true
;
const
double
_kMagnification
=
1.1
;
const
double
_kDatePickerPadSize
=
12.0
;
// Considers setting the default background color from the theme, in the future.
const
Color
_kBackgroundColor
=
CupertinoColors
.
white
;
const
Color
_kBackgroundColor
=
CupertinoColors
.
white
;
// Lays out the date picker based on how much space each single column needs.
//
// Each column is a child of this delegate, indexed from 0 to number of columns - 1.
// Each column will be padded horizontally by 12.0 both left and right.
//
// The picker will be placed in the center, and the leftmost and rightmost
// column will be extended equally to the remaining width.
class
_DatePickerLayoutDelegate
extends
MultiChildLayoutDelegate
{
_DatePickerLayoutDelegate
({
@required
this
.
columnWidths
,
@required
this
.
textDirectionFactor
,
})
:
assert
(
columnWidths
!=
null
),
assert
(
textDirectionFactor
!=
null
);
// The list containing widths of all columns.
final
List
<
double
>
columnWidths
;
// textDirectionFactor is 1 if text is written left to right, and -1 if right to left.
final
int
textDirectionFactor
;
@override
void
performLayout
(
Size
size
)
{
double
remainingWidth
=
size
.
width
;
for
(
int
i
=
0
;
i
<
columnWidths
.
length
;
i
++)
remainingWidth
-=
columnWidths
[
i
]
+
_kDatePickerPadSize
*
2
;
double
currentHorizontalOffset
=
0.0
;
for
(
int
i
=
0
;
i
<
columnWidths
.
length
;
i
++)
{
final
int
index
=
textDirectionFactor
==
1
?
i
:
columnWidths
.
length
-
i
-
1
;
double
childWidth
=
columnWidths
[
index
]
+
_kDatePickerPadSize
*
2
;
if
(
index
==
0
||
index
==
columnWidths
.
length
-
1
)
childWidth
+=
remainingWidth
/
2
;
layoutChild
(
index
,
BoxConstraints
.
tight
(
Size
(
childWidth
,
size
.
height
)));
positionChild
(
index
,
Offset
(
currentHorizontalOffset
,
0.0
));
currentHorizontalOffset
+=
childWidth
;
}
}
@override
bool
shouldRelayout
(
_DatePickerLayoutDelegate
oldDelegate
)
{
return
columnWidths
!=
oldDelegate
.
columnWidths
||
textDirectionFactor
!=
oldDelegate
.
textDirectionFactor
;
}
}
/// Different display modes of [CupertinoDatePicker].
///
/// See also:
///
/// * [CupertinoDatePicker], the class that implements different display modes
/// of the iOS-style date picker.
/// * [CupertinoPicker], the class that implements a content agnostic spinner UI.
enum
CupertinoDatePickerMode
{
/// Mode that shows the date in hour, minute, and (optional) an AM/PM designation.
/// The AM/PM designation is shown only if [CupertinoDatePicker] does not use 24h format.
/// Column order is subject to internationalization.
///
/// Example: [4 | 14 | PM].
time
,
/// Mode that shows the date in month, day of month, and year.
/// Name of month is spelled in full.
/// Column order is subject to internationalization.
///
/// Example: [July | 13 | 2012].
date
,
/// Mode that shows the date as day of the week, month, day of month and
/// the time in hour, minute, and (optional) an AM/PM designation.
/// The AM/PM designation is shown only if [CupertinoDatePicker] does not use 24h format.
/// Column order is subject to internationalization.
///
/// Example: [Fri Jul 13 | 4 | 14 | PM]
dateAndTime
,
}
// Different types of column in CupertinoDatePicker.
enum
_PickerColumnType
{
// Day of month column in date mode.
dayOfMonth
,
// Month column in date mode.
month
,
// Year column in date mode.
year
,
// Medium date column in dateAndTime mode.
date
,
// Hour column in time and dateAndTime mode.
hour
,
// minute column in time and dateAndTime mode.
minute
,
// AM/PM column in time and dateAndTime mode.
dayPeriod
,
}
/// A date picker widget in iOS style.
///
/// There are several modes of the date picker listed in [CupertinoDatePickerMode].
///
/// The class will display its children as consecutive columns. Its children
/// order is based on internationalization.
///
/// Example of the picker in date mode:
///
/// * US-English: [July | 13 | 2012]
/// * Vietnamese: [13 | Tháng 7 | 2012]
///
/// See also:
///
/// * [CupertinoTimerPicker], the class that implements the iOS-style timer picker.
/// * [CupertinoPicker], the class that implements a content agnostic spinner UI.
class
CupertinoDatePicker
extends
StatefulWidget
{
/// Constructs an iOS style date picker.
///
/// [mode] is one of the mode listed in [CupertinoDatePickerMode] and defaults
/// to [CupertinoDatePickerMode.dateAndTime].
///
/// [onDateTimeChanged] is the callback called when the selected date or time
/// changes and must not be null.
///
/// [initialDateTime] is the initial date time of the picker. Defaults to the
/// present date and time and must not be null. The present must conform to
/// the intervals set in [minimumDate], [maximumDate], [minimumYear], and
/// [maximumYear].
///
/// [minimumDate] is the minimum date that the picker can be scrolled to in
/// [CupertinoDatePickerMode.dateAndTime] mode. Null if there's no limit.
///
/// [maximumDate] is the maximum date that the picker can be scrolled to in
/// [CupertinoDatePickerMode.dateAndTime] mode. Null if there's no limit.
///
/// [minimumYear] is the minimum year that the picker can be scrolled to in
/// [CupertinoDatePickerMode.date] mode. Defaults to 1 and must not be null.
///
/// [maximumYear] is the maximum year that the picker can be scrolled to in
/// [CupertinoDatePickerMode.date] mode. Null if there's no limit.
///
/// [minuteInterval] is the granularity of the minute spinner. Must be a
/// positive integer factor of 60.
///
/// [use24hFormat] decides whether 24 hour format is used. Defaults to false.
CupertinoDatePicker
({
this
.
mode
=
CupertinoDatePickerMode
.
dateAndTime
,
@required
this
.
onDateTimeChanged
,
// ignore: always_require_non_null_named_parameters
DateTime
initialDateTime
,
this
.
minimumDate
,
this
.
maximumDate
,
this
.
minimumYear
=
1
,
this
.
maximumYear
,
this
.
minuteInterval
=
1
,
this
.
use24hFormat
=
false
,
})
:
this
.
initialDateTime
=
initialDateTime
??
DateTime
.
now
(),
assert
(
mode
!=
null
),
assert
(
onDateTimeChanged
!=
null
),
assert
(
initialDateTime
!=
null
),
assert
(
mode
!=
CupertinoDatePickerMode
.
dateAndTime
||
minimumDate
==
null
||
!
initialDateTime
.
isBefore
(
minimumDate
),
'initial date is before minimum date'
,
),
assert
(
mode
!=
CupertinoDatePickerMode
.
dateAndTime
||
maximumDate
==
null
||
!
initialDateTime
.
isAfter
(
maximumDate
),
'initial date is after maximum date'
,
),
assert
(
minimumYear
!=
null
),
assert
(
mode
!=
CupertinoDatePickerMode
.
date
||
(
minimumYear
>=
1
&&
initialDateTime
.
year
>=
minimumYear
),
'initial year is not greater than minimum year, or mininum year is not positive'
,
),
assert
(
mode
!=
CupertinoDatePickerMode
.
date
||
maximumYear
==
null
||
initialDateTime
.
year
<=
maximumYear
,
'initial year is not smaller than maximum year'
,
),
assert
(
minuteInterval
>
0
&&
60
%
minuteInterval
==
0
,
'minute interval is not a positive integer factor of 60'
,
),
assert
(
initialDateTime
.
minute
%
minuteInterval
==
0
,
'initial minute is not divisible by minute interval'
,
);
/// The mode of the date picker as one of [CupertinoDatePickerMode].
/// Defaults to [CupertinoDatePickerMode.dateAndTime]. Cannot be null and
/// value cannot change after initial build.
final
CupertinoDatePickerMode
mode
;
/// The initial date and/or time of the picker. Defaults to the present date
/// and time and must not be null. The present must conform to the intervals
/// set in [minimumDate], [maximumDate], [minimumYear], and [maximumYear].
///
/// Changing this value after the initial build will not affect the currently
/// selected date time.
final
DateTime
initialDateTime
;
/// Minimum date that the picker can be scrolled to in
/// [CupertinoDatePickerMode.dateAndTime] mode. Null if there's no limit.
final
DateTime
minimumDate
;
/// Maximum date that the picker can be scrolled to in
/// [CupertinoDatePickerMode.dateAndTime] mode. Null if there's no limit.
final
DateTime
maximumDate
;
/// Minimum year that the picker can be scrolled to in
/// [CupertinoDatePickerMode.date] mode. Defaults to 1 and must not be null.
final
int
minimumYear
;
/// Maximum year that the picker can be scrolled to in
/// [CupertinoDatePickerMode.date] mode. Null if there's no limit.
final
int
maximumYear
;
/// The granularity of the minutes spinner, if it is shown in the current mode.
/// Must be an integer factor of 60.
final
int
minuteInterval
;
/// Whether to use 24 hour format. Defaults to false.
final
bool
use24hFormat
;
/// Callback called when the selected date and/or time changes. Must not be
/// null.
final
ValueChanged
<
DateTime
>
onDateTimeChanged
;
@override
State
<
StatefulWidget
>
createState
()
{
// The `time` mode and `dateAndTime` mode of the picker share the time
// columns, so they are placed together to one state.
// The `date` mode has different children and is implemented in a different
// state.
if
(
mode
==
CupertinoDatePickerMode
.
time
||
mode
==
CupertinoDatePickerMode
.
dateAndTime
)
return
_CupertinoDatePickerDateTimeState
();
else
return
_CupertinoDatePickerDateState
();
}
// Estimate the minimum width that each column needs to layout its content.
static
double
_getColumnWidth
(
_PickerColumnType
columnType
,
CupertinoLocalizations
localizations
,
BuildContext
context
,
)
{
String
longestText
=
''
;
switch
(
columnType
)
{
case
_PickerColumnType
.
date
:
// Measuring the length of all possible date is impossible, so here
// just some dates are measured.
for
(
int
i
=
1
;
i
<=
12
;
i
++)
{
// An arbitrary date.
final
String
date
=
localizations
.
datePickerMediumDate
(
DateTime
(
2018
,
i
,
25
));
if
(
longestText
.
length
<
date
.
length
)
longestText
=
date
;
}
break
;
case
_PickerColumnType
.
hour
:
for
(
int
i
=
0
;
i
<
24
;
i
++)
{
final
String
hour
=
localizations
.
datePickerHour
(
i
);
if
(
longestText
.
length
<
hour
.
length
)
longestText
=
hour
;
}
break
;
case
_PickerColumnType
.
minute
:
for
(
int
i
=
0
;
i
<
60
;
i
++)
{
final
String
minute
=
localizations
.
datePickerMinute
(
i
);
if
(
longestText
.
length
<
minute
.
length
)
longestText
=
minute
;
}
break
;
case
_PickerColumnType
.
dayPeriod
:
longestText
=
localizations
.
anteMeridiemAbbreviation
.
length
>
localizations
.
postMeridiemAbbreviation
.
length
?
localizations
.
anteMeridiemAbbreviation
:
localizations
.
postMeridiemAbbreviation
;
break
;
case
_PickerColumnType
.
dayOfMonth
:
for
(
int
i
=
1
;
i
<=
31
;
i
++)
{
final
String
dayOfMonth
=
localizations
.
datePickerDayOfMonth
(
i
);
if
(
longestText
.
length
<
dayOfMonth
.
length
)
longestText
=
dayOfMonth
;
}
break
;
case
_PickerColumnType
.
month
:
for
(
int
i
=
1
;
i
<=
12
;
i
++)
{
final
String
month
=
localizations
.
datePickerMonth
(
i
);
if
(
longestText
.
length
<
month
.
length
)
longestText
=
month
;
}
break
;
case
_PickerColumnType
.
year
:
longestText
=
localizations
.
datePickerYear
(
2018
);
break
;
}
assert
(
longestText
!=
''
,
'column type is not appropriate'
);
final
TextPainter
painter
=
TextPainter
(
text:
TextSpan
(
style:
DefaultTextStyle
.
of
(
context
).
style
,
text:
longestText
,
),
textDirection:
Directionality
.
of
(
context
),
);
// This operation is expensive and should be avoided. It is called here only
// because there's no other way to get the information we want without
// laying out the text.
painter
.
layout
();
return
painter
.
maxIntrinsicWidth
;
}
}
typedef
_ColumnBuilder
=
Widget
Function
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
);
class
_CupertinoDatePickerDateTimeState
extends
State
<
CupertinoDatePicker
>
{
int
textDirectionFactor
;
CupertinoLocalizations
localizations
;
// Alignment based on text direction. The variable name is self descriptive,
// however, when text direction is rtl, alignment is reversed.
Alignment
alignCenterLeft
;
Alignment
alignCenterRight
;
// Read this out when the state is initially created. Changes in initialDateTime
// in the widget after first build is ignored.
DateTime
initialDateTime
;
// The currently selected values of the date picker.
int
selectedDayFromInitial
;
// The difference in days between the initial date and the currently selected date.
int
selectedHour
;
int
selectedMinute
;
int
selectedAmPm
;
// 0 means AM, 1 means PM.
// The controller of the AM/PM column.
FixedExtentScrollController
amPmController
;
// Estimated width of columns.
final
Map
<
int
,
double
>
estimatedColumnWidths
=
<
int
,
double
>{};
@override
void
initState
()
{
super
.
initState
();
initialDateTime
=
widget
.
initialDateTime
;
selectedDayFromInitial
=
0
;
selectedHour
=
widget
.
initialDateTime
.
hour
;
selectedMinute
=
widget
.
initialDateTime
.
minute
;
selectedAmPm
=
0
;
if
(!
widget
.
use24hFormat
)
{
selectedAmPm
=
selectedHour
~/
12
;
selectedHour
=
selectedHour
%
12
;
if
(
selectedHour
==
0
)
selectedHour
=
12
;
amPmController
=
FixedExtentScrollController
(
initialItem:
selectedAmPm
);
}
}
@override
void
didUpdateWidget
(
CupertinoDatePicker
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
assert
(
oldWidget
.
mode
==
widget
.
mode
,
"The CupertinoDatePicker's mode cannot change once it's built"
,
);
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
textDirectionFactor
=
Directionality
.
of
(
context
)
==
TextDirection
.
ltr
?
1
:
-
1
;
localizations
=
CupertinoLocalizations
.
of
(
context
)
??
const
DefaultCupertinoLocalizations
();
alignCenterLeft
=
textDirectionFactor
==
1
?
Alignment
.
centerLeft
:
Alignment
.
centerRight
;
alignCenterRight
=
textDirectionFactor
==
1
?
Alignment
.
centerRight
:
Alignment
.
centerLeft
;
estimatedColumnWidths
.
clear
();
}
// Lazily calculate the column width of the column being displayed only.
double
_getEstimatedColumnWidth
(
_PickerColumnType
columnType
)
{
if
(
estimatedColumnWidths
[
columnType
.
index
]
==
null
)
{
estimatedColumnWidths
[
columnType
.
index
]
=
CupertinoDatePicker
.
_getColumnWidth
(
columnType
,
localizations
,
context
);
}
return
estimatedColumnWidths
[
columnType
.
index
];
}
// Gets the current date time of the picker.
DateTime
_getDateTime
()
{
final
DateTime
date
=
DateTime
(
initialDateTime
.
year
,
initialDateTime
.
month
,
initialDateTime
.
day
,
).
add
(
Duration
(
days:
selectedDayFromInitial
));
return
DateTime
(
date
.
year
,
date
.
month
,
date
.
day
,
selectedHour
+
selectedAmPm
*
12
,
selectedMinute
,
);
}
// Builds the date column. The date is displayed in medium date format (e.g. Fri Aug 31).
Widget
_buildMediumDatePicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
.
builder
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedDayFromInitial
),
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedDayFromInitial
=
index
;
widget
.
onDateTimeChanged
(
_getDateTime
());
},
itemBuilder:
(
BuildContext
context
,
int
index
)
{
final
DateTime
dateTime
=
DateTime
(
initialDateTime
.
year
,
initialDateTime
.
month
,
initialDateTime
.
day
,
).
add
(
Duration
(
days:
index
));
if
(
widget
.
minimumDate
!=
null
&&
dateTime
.
isBefore
(
widget
.
minimumDate
))
return
null
;
if
(
widget
.
maximumDate
!=
null
&&
dateTime
.
isAfter
(
widget
.
maximumDate
))
return
null
;
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerMediumDate
(
dateTime
)),
);
},
);
}
Widget
_buildHourPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedHour
),
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
if
(
widget
.
use24hFormat
)
{
selectedHour
=
index
;
widget
.
onDateTimeChanged
(
_getDateTime
());
}
else
{
final
int
currentHourIn24h
=
selectedHour
+
selectedAmPm
*
12
;
// Automatically scrolls the am/pm column when the hour column value
// goes far enough. This behavior is similar to
// iOS picker version.
if
(
currentHourIn24h
~/
12
!=
index
~/
12
)
{
selectedHour
=
index
%
12
;
amPmController
.
animateToItem
(
1
-
amPmController
.
selectedItem
,
duration:
const
Duration
(
milliseconds:
300
),
// Set by comparing with iOS version.
curve:
Curves
.
easeOut
,
);
// Set by comparing with iOS version.
}
else
{
selectedHour
=
index
%
12
;
widget
.
onDateTimeChanged
(
_getDateTime
());
}
}
},
children:
List
<
Widget
>.
generate
(
24
,
(
int
index
)
{
int
hour
=
index
;
if
(!
widget
.
use24hFormat
)
hour
=
hour
%
12
==
0
?
12
:
hour
%
12
;
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerHour
(
hour
),
semanticsLabel:
localizations
.
datePickerHourSemanticsLabel
(
hour
),
),
);
}),
looping:
true
,
);
}
Widget
_buildMinutePicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedMinute
),
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedMinute
=
index
*
widget
.
minuteInterval
;
widget
.
onDateTimeChanged
(
_getDateTime
());
},
children:
List
<
Widget
>.
generate
(
60
~/
widget
.
minuteInterval
,
(
int
index
)
{
final
int
minute
=
index
*
widget
.
minuteInterval
;
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerMinute
(
minute
),
semanticsLabel:
localizations
.
datePickerMinuteSemanticsLabel
(
minute
),
),
);
}),
looping:
true
,
);
}
Widget
_buildAmPmPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
(
scrollController:
amPmController
,
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedAmPm
=
index
;
widget
.
onDateTimeChanged
(
_getDateTime
());
},
children:
List
<
Widget
>.
generate
(
2
,
(
int
index
)
{
return
itemPositioningBuilder
(
context
,
Text
(
index
==
0
?
localizations
.
anteMeridiemAbbreviation
:
localizations
.
postMeridiemAbbreviation
),
);
}),
);
}
@override
Widget
build
(
BuildContext
context
)
{
// Widths of the columns in this picker, ordered from left to right.
final
List
<
double
>
columnWidths
=
<
double
>[
_getEstimatedColumnWidth
(
_PickerColumnType
.
hour
),
_getEstimatedColumnWidth
(
_PickerColumnType
.
minute
),
];
final
List
<
_ColumnBuilder
>
pickerBuilders
=
<
_ColumnBuilder
>[
_buildHourPicker
,
_buildMinutePicker
,
];
// Adds am/pm column if the picker is not using 24h format.
if
(!
widget
.
use24hFormat
)
{
if
(
localizations
.
datePickerDateTimeOrder
==
DatePickerDateTimeOrder
.
date_time_dayPeriod
||
localizations
.
datePickerDateTimeOrder
==
DatePickerDateTimeOrder
.
time_dayPeriod_date
)
{
pickerBuilders
.
add
(
_buildAmPmPicker
);
columnWidths
.
add
(
_getEstimatedColumnWidth
(
_PickerColumnType
.
dayPeriod
));
}
else
{
pickerBuilders
.
insert
(
0
,
_buildAmPmPicker
);
columnWidths
.
insert
(
0
,
_getEstimatedColumnWidth
(
_PickerColumnType
.
dayPeriod
));
}
}
// Adds medium date column if the picker's mode is date and time.
if
(
widget
.
mode
==
CupertinoDatePickerMode
.
dateAndTime
)
{
if
(
localizations
.
datePickerDateTimeOrder
==
DatePickerDateTimeOrder
.
time_dayPeriod_date
||
localizations
.
datePickerDateTimeOrder
==
DatePickerDateTimeOrder
.
dayPeriod_time_date
)
{
pickerBuilders
.
add
(
_buildMediumDatePicker
);
columnWidths
.
add
(
_getEstimatedColumnWidth
(
_PickerColumnType
.
date
));
}
else
{
pickerBuilders
.
insert
(
0
,
_buildMediumDatePicker
);
columnWidths
.
insert
(
0
,
_getEstimatedColumnWidth
(
_PickerColumnType
.
date
));
}
}
final
List
<
Widget
>
pickers
=
<
Widget
>[];
// The iOS timer picker has its width fixed to 330.0 in all modes.
for
(
int
i
=
0
;
i
<
columnWidths
.
length
;
i
++)
{
double
offAxisFraction
=
0.0
;
if
(
i
==
0
)
offAxisFraction
=
-
0.5
*
textDirectionFactor
;
else
if
(
i
>=
2
||
columnWidths
.
length
==
2
)
offAxisFraction
=
0.5
*
textDirectionFactor
;
EdgeInsets
padding
=
const
EdgeInsets
.
only
(
right:
_kDatePickerPadSize
);
if
(
i
==
columnWidths
.
length
-
1
)
padding
=
padding
.
flipped
;
if
(
textDirectionFactor
==
-
1
)
padding
=
padding
.
flipped
;
pickers
.
add
(
LayoutId
(
id:
i
,
child:
pickerBuilders
[
i
](
offAxisFraction
,
(
BuildContext
context
,
Widget
child
)
{
return
Container
(
alignment:
i
==
columnWidths
.
length
-
1
?
alignCenterLeft
:
alignCenterRight
,
padding:
padding
,
child:
Container
(
alignment:
i
==
columnWidths
.
length
-
1
?
alignCenterLeft
:
alignCenterRight
,
width:
i
==
0
||
i
==
columnWidths
.
length
-
1
?
null
:
columnWidths
[
i
]
+
_kDatePickerPadSize
,
child:
child
,
),
);
},
),
));
}
return
MediaQuery
(
data:
const
MediaQueryData
(
textScaleFactor:
1.0
),
child:
CustomMultiChildLayout
(
delegate:
_DatePickerLayoutDelegate
(
columnWidths:
columnWidths
,
textDirectionFactor:
textDirectionFactor
,
),
children:
pickers
,
),
);
}
}
class
_CupertinoDatePickerDateState
extends
State
<
CupertinoDatePicker
>
{
int
textDirectionFactor
;
CupertinoLocalizations
localizations
;
// Alignment based on text direction. The variable name is self descriptive,
// however, when text direction is rtl, alignment is reversed.
Alignment
alignCenterLeft
;
Alignment
alignCenterRight
;
// The currently selected values of the picker.
int
selectedDay
;
int
selectedMonth
;
int
selectedYear
;
// The controller of the day picker. There are cases where the selected value
// of the picker is invalid (e.g. February 30th 2018), and this dayController
// is responsible for jumping to a valid value.
FixedExtentScrollController
dayController
;
// Estimated width of columns.
Map
<
int
,
double
>
estimatedColumnWidths
=
<
int
,
double
>{};
@override
void
initState
()
{
super
.
initState
();
selectedDay
=
widget
.
initialDateTime
.
day
;
selectedMonth
=
widget
.
initialDateTime
.
month
;
selectedYear
=
widget
.
initialDateTime
.
year
;
dayController
=
FixedExtentScrollController
(
initialItem:
selectedDay
-
1
);
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
textDirectionFactor
=
Directionality
.
of
(
context
)
==
TextDirection
.
ltr
?
1
:
-
1
;
localizations
=
CupertinoLocalizations
.
of
(
context
)
??
const
DefaultCupertinoLocalizations
();
alignCenterLeft
=
textDirectionFactor
==
1
?
Alignment
.
centerLeft
:
Alignment
.
centerRight
;
alignCenterRight
=
textDirectionFactor
==
1
?
Alignment
.
centerRight
:
Alignment
.
centerLeft
;
estimatedColumnWidths
[
_PickerColumnType
.
dayOfMonth
.
index
]
=
CupertinoDatePicker
.
_getColumnWidth
(
_PickerColumnType
.
dayOfMonth
,
localizations
,
context
);
estimatedColumnWidths
[
_PickerColumnType
.
month
.
index
]
=
CupertinoDatePicker
.
_getColumnWidth
(
_PickerColumnType
.
month
,
localizations
,
context
);
estimatedColumnWidths
[
_PickerColumnType
.
year
.
index
]
=
CupertinoDatePicker
.
_getColumnWidth
(
_PickerColumnType
.
year
,
localizations
,
context
);
}
Widget
_buildDayPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
final
int
daysInCurrentMonth
=
DateTime
(
selectedYear
,
(
selectedMonth
+
1
)
%
12
,
0
).
day
;
return
CupertinoPicker
(
scrollController:
dayController
,
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedDay
=
index
+
1
;
if
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
).
day
==
selectedDay
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
},
children:
List
<
Widget
>.
generate
(
31
,
(
int
index
)
{
TextStyle
disableTextStyle
;
// Null if not out of range.
if
(
index
>=
daysInCurrentMonth
)
{
disableTextStyle
=
const
TextStyle
(
color:
CupertinoColors
.
inactiveGray
);
}
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerDayOfMonth
(
index
+
1
),
style:
disableTextStyle
,
),
);
}),
looping:
true
,
);
}
Widget
_buildMonthPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedMonth
-
1
),
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedMonth
=
index
+
1
;
if
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
).
day
==
selectedDay
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
},
children:
List
<
Widget
>.
generate
(
12
,
(
int
index
)
{
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerMonth
(
index
+
1
)),
);
}),
looping:
true
,
);
}
Widget
_buildYearPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
.
builder
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedYear
),
itemExtent:
_kItemExtent
,
offAxisFraction:
offAxisFraction
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
_kBackgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedYear
=
index
;
if
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
).
day
==
selectedDay
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
},
itemBuilder:
(
BuildContext
context
,
int
index
)
{
if
(
index
<
widget
.
minimumYear
)
return
null
;
if
(
widget
.
maximumYear
!=
null
&&
index
>
widget
.
maximumYear
)
return
null
;
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerYear
(
index
)),
);
},
);
}
bool
_keepInValidRange
(
ScrollEndNotification
notification
)
{
// Whenever scrolling lands on an invalid entry, the picker
// automatically scrolls to a valid one.
final
int
desiredDay
=
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
).
day
;
if
(
desiredDay
!=
selectedDay
)
{
SchedulerBinding
.
instance
.
addPostFrameCallback
((
Duration
timestamp
)
{
dayController
.
animateToItem
(
// The next valid date is also the amount of days overflown.
dayController
.
selectedItem
-
desiredDay
,
duration:
const
Duration
(
milliseconds:
200
),
curve:
Curves
.
easeOut
,
);
});
}
setState
(()
{
// Rebuild because the number of valid days per month are different
// depending on the month and year.
});
return
false
;
}
@override
Widget
build
(
BuildContext
context
)
{
List
<
_ColumnBuilder
>
pickerBuilders
=
<
_ColumnBuilder
>[];
List
<
double
>
columnWidths
=
<
double
>[];
switch
(
localizations
.
datePickerDateOrder
)
{
case
DatePickerDateOrder
.
mdy
:
pickerBuilders
=
<
_ColumnBuilder
>[
_buildMonthPicker
,
_buildDayPicker
,
_buildYearPicker
];
columnWidths
=
<
double
>[
estimatedColumnWidths
[
_PickerColumnType
.
month
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
dayOfMonth
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
year
.
index
]];
break
;
case
DatePickerDateOrder
.
dmy
:
pickerBuilders
=
<
_ColumnBuilder
>[
_buildDayPicker
,
_buildMonthPicker
,
_buildYearPicker
];
columnWidths
=
<
double
>[
estimatedColumnWidths
[
_PickerColumnType
.
dayOfMonth
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
month
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
year
.
index
]];
break
;
case
DatePickerDateOrder
.
ymd
:
pickerBuilders
=
<
_ColumnBuilder
>[
_buildYearPicker
,
_buildMonthPicker
,
_buildDayPicker
];
columnWidths
=
<
double
>[
estimatedColumnWidths
[
_PickerColumnType
.
year
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
month
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
dayOfMonth
.
index
]];
break
;
case
DatePickerDateOrder
.
ydm
:
pickerBuilders
=
<
_ColumnBuilder
>[
_buildYearPicker
,
_buildDayPicker
,
_buildMonthPicker
];
columnWidths
=
<
double
>[
estimatedColumnWidths
[
_PickerColumnType
.
year
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
dayOfMonth
.
index
],
estimatedColumnWidths
[
_PickerColumnType
.
month
.
index
]];
break
;
default
:
assert
(
false
,
'date order is not specified'
);
}
final
List
<
Widget
>
pickers
=
<
Widget
>[];
for
(
int
i
=
0
;
i
<
columnWidths
.
length
;
i
++)
{
final
double
offAxisFraction
=
(
i
-
1
)
*
0.3
*
textDirectionFactor
;
EdgeInsets
padding
=
const
EdgeInsets
.
only
(
right:
_kDatePickerPadSize
);
if
(
textDirectionFactor
==
-
1
)
padding
=
const
EdgeInsets
.
only
(
left:
_kDatePickerPadSize
);
pickers
.
add
(
LayoutId
(
id:
i
,
child:
pickerBuilders
[
i
](
offAxisFraction
,
(
BuildContext
context
,
Widget
child
)
{
return
Container
(
alignment:
i
==
columnWidths
.
length
-
1
?
alignCenterLeft
:
alignCenterRight
,
padding:
i
==
0
?
null
:
padding
,
child:
Container
(
alignment:
i
==
0
?
alignCenterLeft
:
alignCenterRight
,
width:
columnWidths
[
i
]
+
_kDatePickerPadSize
,
child:
child
,
),
);
},
),
));
}
return
MediaQuery
(
data:
const
MediaQueryData
(
textScaleFactor:
1.0
),
child:
NotificationListener
<
ScrollEndNotification
>(
onNotification:
_keepInValidRange
,
child:
CustomMultiChildLayout
(
delegate:
_DatePickerLayoutDelegate
(
columnWidths:
columnWidths
,
textDirectionFactor:
textDirectionFactor
,
),
children:
pickers
,
),
),
);
}
}
// The iOS date picker and timer picker has their width fixed to 330.0 in all
// modes.
//
//
// If the maximum width given to the picker is greater than 330.0, the leftmost
// If the maximum width given to the picker is greater than 330.0, the leftmost
// and rightmost column will be extended equally so that the widths match, and
// and rightmost column will be extended equally so that the widths match, and
// the picker is in the center.
// the picker is in the center.
//
//
// If the maximum width given to the picker is smaller than 330.0, the picker
i
s
// If the maximum width given to the picker is smaller than 330.0, the picker
'
s
//
placed in the center and both left side and right side are clipped
.
//
layout will be broken
.
/// Different modes of [CupertinoTimerPicker].
/// Different modes of [CupertinoTimerPicker].
///
/// See also:
///
/// * [CupertinoTimerPicker], the class that implements the iOS-style timer picker.
/// * [CupertinoPicker], the class that implements a content agnostic spinner UI.
enum
CupertinoTimerPickerMode
{
enum
CupertinoTimerPickerMode
{
/// Mode that shows the timer duration in hour and minute.
/// Mode that shows the timer duration in hour and minute.
///
///
...
@@ -47,14 +923,20 @@ enum CupertinoTimerPickerMode {
...
@@ -47,14 +923,20 @@ enum CupertinoTimerPickerMode {
/// The duration is bound between 0 and 23 hours 59 minutes 59 seconds.
/// The duration is bound between 0 and 23 hours 59 minutes 59 seconds.
///
///
/// There are several modes of the timer picker listed in [CupertinoTimerPickerMode].
/// There are several modes of the timer picker listed in [CupertinoTimerPickerMode].
///
/// See also:
///
/// * [CupertinoDatePicker], the class that implements different display modes
/// of the iOS-style date picker.
/// * [CupertinoPicker], the class that implements a content agnostic spinner UI.
class
CupertinoTimerPicker
extends
StatefulWidget
{
class
CupertinoTimerPicker
extends
StatefulWidget
{
/// Constructs an iOS style countdown timer picker.
/// Constructs an iOS style countdown timer picker.
///
///
/// [mode] is one of the modes listed in [CupertinoTimerPickerMode] and
/// [mode] is one of the modes listed in [CupertinoTimerPickerMode] and
/// defaults to [CupertinoTimerPickerMode.hms].
/// defaults to [CupertinoTimerPickerMode.hms].
///
///
/// [onTimerDurationChanged] is the callback
when the selected duration changes
/// [onTimerDurationChanged] is the callback
called when the selected duration
/// and must not be null.
///
changes
and must not be null.
///
///
/// [initialTimerDuration] defaults to 0 second and is limited from 0 second
/// [initialTimerDuration] defaults to 0 second and is limited from 0 second
/// to 23 hours 59 minutes 59 seconds.
/// to 23 hours 59 minutes 59 seconds.
...
@@ -93,7 +975,7 @@ class CupertinoTimerPicker extends StatefulWidget {
...
@@ -93,7 +975,7 @@ class CupertinoTimerPicker extends StatefulWidget {
/// of 60.
/// of 60.
final
int
secondInterval
;
final
int
secondInterval
;
/// Callback when the timer duration changes.
/// Callback
called
when the timer duration changes.
final
ValueChanged
<
Duration
>
onTimerDurationChanged
;
final
ValueChanged
<
Duration
>
onTimerDurationChanged
;
@override
@override
...
@@ -304,8 +1186,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
...
@@ -304,8 +1186,7 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
),
),
),
),
);
);
}
}
else
{
else
{
minuteLabel
=
IgnorePointer
(
minuteLabel
=
IgnorePointer
(
child:
Container
(
child:
Container
(
alignment:
alignCenterRight
,
alignment:
alignCenterRight
,
...
@@ -420,16 +1301,14 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
...
@@ -420,16 +1301,14 @@ class _CupertinoTimerPickerState extends State<CupertinoTimerPicker> {
Expanded
(
child:
_buildMinuteColumn
()),
Expanded
(
child:
_buildMinuteColumn
()),
],
],
);
);
}
}
else
if
(
widget
.
mode
==
CupertinoTimerPickerMode
.
ms
)
{
else
if
(
widget
.
mode
==
CupertinoTimerPickerMode
.
ms
)
{
picker
=
Row
(
picker
=
Row
(
children:
<
Widget
>[
children:
<
Widget
>[
Expanded
(
child:
_buildMinuteColumn
()),
Expanded
(
child:
_buildMinuteColumn
()),
Expanded
(
child:
_buildSecondColumn
()),
Expanded
(
child:
_buildSecondColumn
()),
],
],
);
);
}
}
else
{
else
{
picker
=
Row
(
picker
=
Row
(
children:
<
Widget
>[
children:
<
Widget
>[
Expanded
(
child:
_buildHourColumn
()),
Expanded
(
child:
_buildHourColumn
()),
...
...
packages/flutter/lib/src/cupertino/localizations.dart
View file @
2a8e35cc
...
@@ -7,6 +7,46 @@ import 'dart:async';
...
@@ -7,6 +7,46 @@ import 'dart:async';
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
/// Determines the order of the columns inside [CupertinoDatePicker] in
/// time and date time mode.
enum
DatePickerDateTimeOrder
{
/// Order of the columns, from left to right: date, hour, minute, am/pm.
///
/// Example: [Fri Aug 31 | 02 | 08 | PM].
date_time_dayPeriod
,
/// Order of the columns, from left to right: date, am/pm, hour, minute.
///
/// Example: [Fri Aug 31 | PM | 02 | 08].
date_dayPeriod_time
,
/// Order of the columns, from left to right: hour, minute, am/pm, date.
///
/// Example: [02 | 08 | PM | Fri Aug 31].
time_dayPeriod_date
,
/// Order of the columns, from left to right: am/pm, hour, minute, date.
///
/// Example: [PM | 02 | 08 | Fri Aug 31].
dayPeriod_time_date
,
}
/// Determines the order of the columns inside [CupertinoDatePicker] in date mode.
enum
DatePickerDateOrder
{
/// Order of the columns, from left to right: day, month, year.
///
/// Example: [12 | March | 1996]
dmy
,
/// Order of the columns, from left to right: month, day, year.
///
/// Example: [March | 12 | 1996]
mdy
,
/// Order of the columns, from left to right: year, month, day.
///
/// Example: [1996 | March | 12]
ymd
,
/// Order of the columns, from left to right: year, day, month.
///
/// Example: [1996 | 12 | March]
ydm
,
}
/// Defines the localized resource values used by the Cupertino widgets.
/// Defines the localized resource values used by the Cupertino widgets.
///
///
...
@@ -61,6 +101,9 @@ abstract class CupertinoLocalizations {
...
@@ -61,6 +101,9 @@ abstract class CupertinoLocalizations {
/// - Arabic: ٠١
/// - Arabic: ٠١
String
datePickerHour
(
int
hour
);
String
datePickerHour
(
int
hour
);
/// Semantics label for the given hour value in [CupertinoDatePicker].
String
datePickerHourSemanticsLabel
(
int
hour
);
/// Minute that is shown in [CupertinoDatePicker] spinner corresponding
/// Minute that is shown in [CupertinoDatePicker] spinner corresponding
/// to the given minute value.
/// to the given minute value.
///
///
...
@@ -70,9 +113,14 @@ abstract class CupertinoLocalizations {
...
@@ -70,9 +113,14 @@ abstract class CupertinoLocalizations {
/// - Arabic: ٠١
/// - Arabic: ٠١
String
datePickerMinute
(
int
minute
);
String
datePickerMinute
(
int
minute
);
/// Semantics label for the given minute value in [CupertinoDatePicker].
String
datePickerMinuteSemanticsLabel
(
int
minute
);
/// The order of the date elements that will be shown in [CupertinoDatePicker].
/// The order of the date elements that will be shown in [CupertinoDatePicker].
/// Can be any permutation of 'DMY' ('D': day, 'M': month, 'Y': year).
DatePickerDateOrder
get
datePickerDateOrder
;
String
get
datePickerDateOrder
;
/// The order of the time elements that will be shown in [CupertinoDatePicker].
DatePickerDateTimeOrder
get
datePickerDateTimeOrder
;
/// The abbreviation for ante meridiem (before noon) shown in the time picker.
/// The abbreviation for ante meridiem (before noon) shown in the time picker.
String
get
anteMeridiemAbbreviation
;
String
get
anteMeridiemAbbreviation
;
...
@@ -216,9 +264,19 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations {
...
@@ -216,9 +264,19 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations {
@override
@override
String
datePickerHour
(
int
hour
)
=>
hour
.
toString
().
padLeft
(
2
,
'0'
);
String
datePickerHour
(
int
hour
)
=>
hour
.
toString
().
padLeft
(
2
,
'0'
);
@override
String
datePickerHourSemanticsLabel
(
int
hour
)
=>
hour
.
toString
()
+
" o'clock"
;
@override
@override
String
datePickerMinute
(
int
minute
)
=>
minute
.
toString
().
padLeft
(
2
,
'0'
);
String
datePickerMinute
(
int
minute
)
=>
minute
.
toString
().
padLeft
(
2
,
'0'
);
@override
String
datePickerMinuteSemanticsLabel
(
int
minute
)
{
if
(
minute
==
1
)
return
'1 minute'
;
return
minute
.
toString
()
+
' minutes'
;
}
@override
@override
String
datePickerMediumDate
(
DateTime
date
)
{
String
datePickerMediumDate
(
DateTime
date
)
{
return
'
${_shortWeekdays[date.weekday - DateTime.monday]}
'
return
'
${_shortWeekdays[date.weekday - DateTime.monday]}
'
...
@@ -227,7 +285,10 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations {
...
@@ -227,7 +285,10 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations {
}
}
@override
@override
String
get
datePickerDateOrder
=>
'MDY'
;
DatePickerDateOrder
get
datePickerDateOrder
=>
DatePickerDateOrder
.
mdy
;
@override
DatePickerDateTimeOrder
get
datePickerDateTimeOrder
=>
DatePickerDateTimeOrder
.
date_time_dayPeriod
;
@override
@override
String
get
anteMeridiemAbbreviation
=>
'AM'
;
String
get
anteMeridiemAbbreviation
=>
'AM'
;
...
...
packages/flutter/test/cupertino/date_picker_test.dart
View file @
2a8e35cc
...
@@ -97,36 +97,6 @@ void main() {
...
@@ -97,36 +97,6 @@ void main() {
);
);
});
});
testWidgets
(
'secondInterval is positive and is a factor of 60'
,
(
WidgetTester
tester
)
async
{
expect
(
()
{
CupertinoTimerPicker
(
onTimerDurationChanged:
(
_
)
{},
secondInterval:
0
,
);
},
throwsAssertionError
,
);
expect
(
()
{
CupertinoTimerPicker
(
onTimerDurationChanged:
(
_
)
{},
secondInterval:
-
1
,
);
},
throwsAssertionError
,
);
expect
(
()
{
CupertinoTimerPicker
(
onTimerDurationChanged:
(
_
)
{},
secondInterval:
7
,
);
},
throwsAssertionError
,
);
});
testWidgets
(
'columns are ordered correctly when text direction is ltr'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'columns are ordered correctly when text direction is ltr'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
Directionality
(
Directionality
(
...
@@ -223,4 +193,364 @@ void main() {
...
@@ -223,4 +193,364 @@ void main() {
);
);
});
});
});
});
group
(
'Date picker'
,
()
{
testWidgets
(
'mode is not null'
,
(
WidgetTester
tester
)
async
{
expect
(
()
{
CupertinoDatePicker
(
mode:
null
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
.
now
(),
);
},
throwsAssertionError
,
);
});
testWidgets
(
'onDateTimeChanged is not null'
,
(
WidgetTester
tester
)
async
{
expect
(
()
{
CupertinoDatePicker
(
onDateTimeChanged:
null
,
initialDateTime:
DateTime
.
now
(),
);
},
throwsAssertionError
,
);
});
testWidgets
(
'initial date time is not null'
,
(
WidgetTester
tester
)
async
{
expect
(
()
{
CupertinoDatePicker
(
onDateTimeChanged:
(
_
)
{},
initialDateTime:
null
,
);
},
throwsAssertionError
,
);
});
testWidgets
(
'initial date time is not null'
,
(
WidgetTester
tester
)
async
{
expect
(
()
{
CupertinoDatePicker
(
onDateTimeChanged:
(
_
)
{},
initialDateTime:
null
,
);
},
throwsAssertionError
,
);
});
testWidgets
(
'changing initialDateTime after first build does not do anything'
,
(
WidgetTester
tester
)
async
{
DateTime
selectedDateTime
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
dateAndTime
,
onDateTimeChanged:
(
DateTime
dateTime
)
=>
selectedDateTime
=
dateTime
,
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
await
tester
.
drag
(
find
.
text
(
'10'
),
const
Offset
(
0.0
,
32.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
selectedDateTime
,
DateTime
(
2018
,
1
,
1
,
9
,
30
));
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
dateAndTime
,
onDateTimeChanged:
(
DateTime
dateTime
)
=>
selectedDateTime
=
dateTime
,
// Change the initial date, but it shouldn't affect the present state.
initialDateTime:
DateTime
(
2016
,
4
,
5
,
15
,
00
),
),
),
),
);
await
tester
.
drag
(
find
.
text
(
'09'
),
const
Offset
(
0.0
,
32.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
// Moving up an hour is still based on the original initial date time.
expect
(
selectedDateTime
,
DateTime
(
2018
,
1
,
1
,
8
,
30
));
});
testWidgets
(
'width of picker in date and time mode is consistent'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
dateAndTime
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
// Distance between the first column and the last column.
final
double
distance
=
tester
.
getCenter
(
find
.
text
(
'Mon Jan 1'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'AM'
)).
dx
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
800.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
dateAndTime
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
// Distance between the first and the last column should be the same.
expect
(
tester
.
getCenter
(
find
.
text
(
'Mon Jan 1'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'AM'
)).
dx
,
distance
,
);
});
testWidgets
(
'width of picker in date mode is consistent'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
date
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
// Distance between the first column and the last column.
final
double
distance
=
tester
.
getCenter
(
find
.
text
(
'January'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'2018'
)).
dx
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
800.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
date
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
// Distance between the first and the last column should be the same.
expect
(
tester
.
getCenter
(
find
.
text
(
'January'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'2018'
)).
dx
,
distance
,
);
});
testWidgets
(
'width of picker in time mode is consistent'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
time
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
// Distance between the first column and the last column.
final
double
distance
=
tester
.
getCenter
(
find
.
text
(
'10'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'AM'
)).
dx
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
800.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
time
,
onDateTimeChanged:
(
_
)
{},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
10
,
30
),
),
),
),
);
// Distance between the first and the last column should be the same.
expect
(
tester
.
getCenter
(
find
.
text
(
'10'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'AM'
)).
dx
,
distance
,
);
});
testWidgets
(
'picker automatically scrolls away from invalid date on month change'
,
(
WidgetTester
tester
)
async
{
DateTime
date
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
date
,
onDateTimeChanged:
(
DateTime
newDate
)
{
date
=
newDate
;
},
initialDateTime:
DateTime
(
2018
,
3
,
30
),
),
),
),
);
await
tester
.
drag
(
find
.
text
(
'March'
),
const
Offset
(
0.0
,
32.0
));
// Momentarily, the 2018 and the incorrect 30 of February is aligned.
expect
(
tester
.
getTopLeft
(
find
.
text
(
'2018'
)).
dy
,
tester
.
getTopLeft
(
find
.
text
(
'30'
)).
dy
,
);
await
tester
.
pump
();
// Once to trigger the post frame animate call.
await
tester
.
pump
();
// Once to start the DrivenScrollActivity.
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
date
,
DateTime
(
2018
,
2
,
28
),
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'2018'
)).
dy
,
tester
.
getTopLeft
(
find
.
text
(
'28'
)).
dy
,
);
});
testWidgets
(
'picker automatically scrolls away from invalid date on day change'
,
(
WidgetTester
tester
)
async
{
DateTime
date
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
date
,
onDateTimeChanged:
(
DateTime
newDate
)
{
date
=
newDate
;
},
initialDateTime:
DateTime
(
2018
,
2
,
27
),
// 2018 has 28 days in Feb.
),
),
),
);
await
tester
.
drag
(
find
.
text
(
'27'
),
const
Offset
(
0.0
,
-
32.0
));
await
tester
.
pump
();
expect
(
date
,
DateTime
(
2018
,
2
,
28
),
);
await
tester
.
drag
(
find
.
text
(
'28'
),
const
Offset
(
0.0
,
-
32.0
));
await
tester
.
pump
();
// Once to trigger the post frame animate call.
// Callback doesn't transiently go into invalid dates.
expect
(
date
,
DateTime
(
2018
,
2
,
28
),
);
// Momentarily, the invalid 29th of Feb is dragged into the middle.
expect
(
tester
.
getTopLeft
(
find
.
text
(
'2018'
)).
dy
,
tester
.
getTopLeft
(
find
.
text
(
'29'
)).
dy
,
);
await
tester
.
pump
();
// Once to start the DrivenScrollActivity.
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
date
,
DateTime
(
2018
,
2
,
28
),
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'2018'
)).
dy
,
tester
.
getTopLeft
(
find
.
text
(
'28'
)).
dy
,
);
});
testWidgets
(
'picker automatically scrolls the am/pm column when the hour column changes enough'
,
(
WidgetTester
tester
)
async
{
DateTime
date
;
await
tester
.
pumpWidget
(
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
time
,
onDateTimeChanged:
(
DateTime
newDate
)
{
date
=
newDate
;
},
initialDateTime:
DateTime
(
2018
,
1
,
1
,
11
,
59
),
),
),
),
);
await
tester
.
drag
(
find
.
text
(
'11'
),
const
Offset
(
0.0
,
-
32.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
date
,
DateTime
(
2018
,
1
,
1
,
12
,
59
));
await
tester
.
drag
(
find
.
text
(
'12'
),
const
Offset
(
0.0
,
32.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
date
,
DateTime
(
2018
,
1
,
1
,
11
,
59
));
await
tester
.
drag
(
find
.
text
(
'11'
),
const
Offset
(
0.0
,
64.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
date
,
DateTime
(
2018
,
1
,
1
,
9
,
59
));
await
tester
.
drag
(
find
.
text
(
'09'
),
const
Offset
(
0.0
,
-
192.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
date
,
DateTime
(
2018
,
1
,
1
,
15
,
59
));
});
});
}
}
\ No newline at end of file
packages/flutter/test/cupertino/localizations_test.dart
View file @
2a8e35cc
...
@@ -13,9 +13,12 @@ void main() {
...
@@ -13,9 +13,12 @@ void main() {
expect
(
localizations
.
datePickerMonth
(
1
),
isNotNull
);
expect
(
localizations
.
datePickerMonth
(
1
),
isNotNull
);
expect
(
localizations
.
datePickerDayOfMonth
(
1
),
isNotNull
);
expect
(
localizations
.
datePickerDayOfMonth
(
1
),
isNotNull
);
expect
(
localizations
.
datePickerHour
(
0
),
isNotNull
);
expect
(
localizations
.
datePickerHour
(
0
),
isNotNull
);
expect
(
localizations
.
datePickerHourSemanticsLabel
(
0
),
isNotNull
);
expect
(
localizations
.
datePickerMinute
(
0
),
isNotNull
);
expect
(
localizations
.
datePickerMinute
(
0
),
isNotNull
);
expect
(
localizations
.
datePickerMinuteSemanticsLabel
(
0
),
isNotNull
);
expect
(
localizations
.
datePickerMediumDate
(
DateTime
.
now
()),
isNotNull
);
expect
(
localizations
.
datePickerMediumDate
(
DateTime
.
now
()),
isNotNull
);
expect
(
localizations
.
datePickerDateOrder
,
isNotNull
);
expect
(
localizations
.
datePickerDateOrder
,
isNotNull
);
expect
(
localizations
.
datePickerDateTimeOrder
,
isNotNull
);
expect
(
localizations
.
anteMeridiemAbbreviation
,
isNotNull
);
expect
(
localizations
.
anteMeridiemAbbreviation
,
isNotNull
);
expect
(
localizations
.
postMeridiemAbbreviation
,
isNotNull
);
expect
(
localizations
.
postMeridiemAbbreviation
,
isNotNull
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment