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
a9fc9e56
Unverified
Commit
a9fc9e56
authored
Nov 05, 2019
by
LongCatIsLooong
Committed by
GitHub
Nov 05, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Apply minimumDate & maximumDate constraints in CupertinoDatePicker date mode (#44149)
parent
7dceec21
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
328 additions
and
117 deletions
+328
-117
date_picker.dart
packages/flutter/lib/src/cupertino/date_picker.dart
+236
-115
date_picker_test.dart
packages/flutter/test/cupertino/date_picker_test.dart
+92
-2
No files found.
packages/flutter/lib/src/cupertino/date_picker.dart
View file @
a9fc9e56
...
...
@@ -45,8 +45,9 @@ const double _kTimerPickerColumnIntrinsicWidth = 106;
// for now.
const
double
_kTimerPickerNumberLabelFontSize
=
23
;
TextStyle
_themeTextStyle
(
BuildContext
context
)
{
return
CupertinoTheme
.
of
(
context
).
textTheme
.
dateTimePickerTextStyle
;
TextStyle
_themeTextStyle
(
BuildContext
context
,
{
bool
isValid
=
true
})
{
final
TextStyle
style
=
CupertinoTheme
.
of
(
context
).
textTheme
.
dateTimePickerTextStyle
;
return
isValid
?
style
:
style
.
copyWith
(
color:
CupertinoDynamicColor
.
resolve
(
CupertinoColors
.
inactiveGray
,
context
));
}
// Lays out the date picker based on how much space each single column needs.
...
...
@@ -129,20 +130,20 @@ enum CupertinoDatePickerMode {
/// The AM/PM designation is shown only if [CupertinoDatePicker] does not use 24h format.
/// Column order is subject to internationalization.
///
/// Example:
[4 | 14 | PM]
.
/// 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]
.
/// 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]
/// Example:
` Fri Jul 13 | 4 | 14 | PM `
dateAndTime
,
}
...
...
@@ -173,8 +174,8 @@ enum _PickerColumnType {
///
/// Example of the picker in date mode:
///
/// * US-English:
[July | 13 | 2012]
/// * Vietnamese:
[13 | Tháng 7 | 2012]
/// * US-English:
` July | 13 | 2012 `
/// * Vietnamese:
` 13 | Tháng 7 | 2012 `
///
/// Can be used with [showCupertinoModalPopup] to display the picker modally at
/// the bottom of the screen.
...
...
@@ -202,10 +203,12 @@ class CupertinoDatePicker extends StatefulWidget {
/// [maximumYear].
///
/// [minimumDate] is the minimum date that the picker can be scrolled to in
/// [CupertinoDatePickerMode.dateAndTime] mode. Null if there's no limit.
/// [CupertinoDatePickerMode.date] and [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.
/// [CupertinoDatePickerMode.date] and [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.
...
...
@@ -254,6 +257,14 @@ class CupertinoDatePicker extends StatefulWidget {
mode
!=
CupertinoDatePickerMode
.
date
||
maximumYear
==
null
||
this
.
initialDateTime
.
year
<=
maximumYear
,
'initial year is not smaller than maximum year'
,
);
assert
(
mode
!=
CupertinoDatePickerMode
.
date
||
minimumDate
==
null
||
!
minimumDate
.
isAfter
(
this
.
initialDateTime
),
'initial date
${this.initialDateTime}
is not greater than or euqal to minimumDate
$minimumDate
'
,
);
assert
(
mode
!=
CupertinoDatePickerMode
.
date
||
maximumDate
==
null
||
!
maximumDate
.
isBefore
(
this
.
initialDateTime
),
'initial date
${this.initialDateTime}
is not less than or euqal to maximumDate
$maximumDate
'
,
);
assert
(
this
.
initialDateTime
.
minute
%
minuteInterval
==
0
,
'initial minute is not divisible by minute interval'
,
...
...
@@ -273,12 +284,12 @@ class CupertinoDatePicker extends StatefulWidget {
/// 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.
/// Minimum date that the picker can be scrolled to in
[CupertinoDatePickerMode.date]
///
and
[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.
/// Maximum date that the picker can be scrolled to in
[CupertinoDatePickerMode.date]
///
and
[CupertinoDatePickerMode.dateAndTime] mode. Null if there's no limit.
final
DateTime
maximumDate
;
/// Minimum year that the picker can be scrolled to in
...
...
@@ -311,10 +322,16 @@ class CupertinoDatePicker extends StatefulWidget {
// 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
();
switch
(
mode
)
{
case
CupertinoDatePickerMode
.
time
:
case
CupertinoDatePickerMode
.
dateAndTime
:
return
_CupertinoDatePickerDateTimeState
();
case
CupertinoDatePickerMode
.
date
:
return
_CupertinoDatePickerDateState
();
}
assert
(
false
);
return
_CupertinoDatePickerDateTimeState
();
}
// Estimate the minimum width that each column needs to layout its content.
...
...
@@ -796,6 +813,14 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
// of the picker is invalid (e.g. February 30th 2018), and this dayController
// is responsible for jumping to a valid value.
FixedExtentScrollController
dayController
;
FixedExtentScrollController
monthController
;
FixedExtentScrollController
yearController
;
bool
isDayPickerScrolling
=
false
;
bool
isMonthPickerScrolling
=
false
;
bool
isYearPickerScrolling
=
false
;
bool
get
isScrolling
=>
isDayPickerScrolling
||
isMonthPickerScrolling
||
isYearPickerScrolling
;
// Estimated width of columns.
Map
<
int
,
double
>
estimatedColumnWidths
=
<
int
,
double
>{};
...
...
@@ -808,6 +833,8 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
selectedYear
=
widget
.
initialDateTime
.
year
;
dayController
=
FixedExtentScrollController
(
initialItem:
selectedDay
-
1
);
monthController
=
FixedExtentScrollController
(
initialItem:
selectedMonth
-
1
);
yearController
=
FixedExtentScrollController
(
initialItem:
selectedYear
);
PaintingBinding
.
instance
.
systemFonts
.
addListener
(
_handleSystemFontsChange
);
}
...
...
@@ -821,6 +848,10 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
@override
void
dispose
()
{
dayController
.
dispose
();
monthController
.
dispose
();
yearController
.
dispose
();
PaintingBinding
.
instance
.
systemFonts
.
removeListener
(
_handleSystemFontsChange
);
super
.
dispose
();
}
...
...
@@ -844,115 +875,208 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
estimatedColumnWidths
[
_PickerColumnType
.
year
.
index
]
=
CupertinoDatePicker
.
_getColumnWidth
(
_PickerColumnType
.
year
,
localizations
,
context
);
}
// The DateTime of the last day of a given month in a given year.
// Let `DateTime` handle the year/month overflow.
DateTime
_lastDayInMonth
(
int
year
,
int
month
)
=>
DateTime
(
year
,
month
+
1
,
0
);
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:
widget
.
backgroundColor
,
squeeze:
_kSqueeze
,
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
textStyle
=
_themeTextStyle
(
context
);
if
(
index
>=
daysInCurrentMonth
)
{
textStyle
=
textStyle
.
copyWith
(
color:
CupertinoColors
.
inactiveGray
);
final
int
daysInCurrentMonth
=
_lastDayInMonth
(
selectedYear
,
selectedMonth
).
day
;
return
NotificationListener
<
ScrollNotification
>(
onNotification:
(
ScrollNotification
notification
)
{
if
(
notification
is
ScrollStartNotification
)
{
isDayPickerScrolling
=
true
;
}
else
if
(
notification
is
ScrollEndNotification
)
{
isDayPickerScrolling
=
false
;
_pickerDidStopScrolling
();
}
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerDayOfMonth
(
index
+
1
),
style:
textStyle
,
),
);
}),
looping:
true
,
return
false
;
},
child:
CupertinoPicker
(
scrollController:
dayController
,
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
widget
.
backgroundColor
,
squeeze:
_kSqueeze
,
onSelectedItemChanged:
(
int
index
)
{
selectedDay
=
index
+
1
;
if
(
_isCurrentDateValid
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
},
children:
List
<
Widget
>.
generate
(
31
,
(
int
index
)
{
final
int
day
=
index
+
1
;
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerDayOfMonth
(
day
),
style:
_themeTextStyle
(
context
,
isValid:
day
<=
daysInCurrentMonth
),
),
);
}),
looping:
true
,
),
);
}
Widget
_buildMonthPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedMonth
-
1
),
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
widget
.
backgroundColor
,
squeeze:
_kSqueeze
,
onSelectedItemChanged:
(
int
index
)
{
selectedMonth
=
index
+
1
;
if
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
).
day
==
selectedDay
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
return
NotificationListener
<
ScrollNotification
>(
onNotification:
(
ScrollNotification
notification
)
{
if
(
notification
is
ScrollStartNotification
)
{
isMonthPickerScrolling
=
true
;
}
else
if
(
notification
is
ScrollEndNotification
)
{
isMonthPickerScrolling
=
false
;
_pickerDidStopScrolling
();
}
return
false
;
},
children:
List
<
Widget
>.
generate
(
12
,
(
int
index
)
{
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerMonth
(
index
+
1
),
style:
_themeTextStyle
(
context
),
),
);
}),
looping:
true
,
child:
CupertinoPicker
(
scrollController:
monthController
,
offAxisFraction:
offAxisFraction
,
itemExtent:
_kItemExtent
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
widget
.
backgroundColor
,
squeeze:
_kSqueeze
,
onSelectedItemChanged:
(
int
index
)
{
selectedMonth
=
index
+
1
;
if
(
_isCurrentDateValid
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
},
children:
List
<
Widget
>.
generate
(
12
,
(
int
index
)
{
final
int
month
=
index
+
1
;
final
bool
isInvalidMonth
=
(
widget
?.
minimumDate
?.
year
==
selectedYear
&&
widget
.
minimumDate
.
month
>
month
)
||
(
widget
?.
maximumDate
?.
year
==
selectedYear
&&
widget
.
maximumDate
.
month
<
month
);
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerMonth
(
month
),
style:
_themeTextStyle
(
context
,
isValid:
!
isInvalidMonth
),
),
);
}),
looping:
true
,
),
);
}
Widget
_buildYearPicker
(
double
offAxisFraction
,
TransitionBuilder
itemPositioningBuilder
)
{
return
CupertinoPicker
.
builder
(
scrollController:
FixedExtentScrollController
(
initialItem:
selectedYear
),
itemExtent:
_kItemExtent
,
offAxisFraction:
offAxisFraction
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
widget
.
backgroundColor
,
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
NotificationListener
<
ScrollNotification
>(
onNotification:
(
ScrollNotification
notification
)
{
if
(
notification
is
ScrollStartNotification
)
{
isYearPickerScrolling
=
true
;
}
else
if
(
notification
is
ScrollEndNotification
)
{
isYearPickerScrolling
=
false
;
_pickerDidStopScrolling
();
}
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerYear
(
index
),
style:
_themeTextStyle
(
context
),
),
);
return
false
;
},
child:
CupertinoPicker
.
builder
(
scrollController:
yearController
,
itemExtent:
_kItemExtent
,
offAxisFraction:
offAxisFraction
,
useMagnifier:
_kUseMagnifier
,
magnification:
_kMagnification
,
backgroundColor:
widget
.
backgroundColor
,
onSelectedItemChanged:
(
int
index
)
{
selectedYear
=
index
;
if
(
_isCurrentDateValid
)
widget
.
onDateTimeChanged
(
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
));
},
itemBuilder:
(
BuildContext
context
,
int
year
)
{
if
(
year
<
widget
.
minimumYear
)
return
null
;
if
(
widget
.
maximumYear
!=
null
&&
year
>
widget
.
maximumYear
)
return
null
;
final
bool
isValidYear
=
(
widget
?.
minimumDate
==
null
||
widget
.
minimumDate
.
year
<=
year
)
&&
(
widget
?.
maximumDate
==
null
||
widget
.
maximumDate
.
year
>=
year
);
return
itemPositioningBuilder
(
context
,
Text
(
localizations
.
datePickerYear
(
year
),
style:
_themeTextStyle
(
context
,
isValid:
isValidYear
),
),
);
},
),
);
}
bool
_keepInValidRange
(
ScrollEndNotification
notification
)
{
bool
get
_isCurrentDateValid
{
final
DateTime
selectedDate
=
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
);
final
bool
minCheck
=
widget
.
minimumDate
?.
isAfter
(
selectedDate
)
??
false
;
final
bool
maxCheck
=
widget
.
maximumDate
?.
isBefore
(
selectedDate
)
??
false
;
return
!
minCheck
&&
!
maxCheck
&&
selectedDate
.
day
==
selectedDay
;
}
// One or more pickers have just stopped scrolling.
void
_pickerDidStopScrolling
()
{
// Call setState to update the greyed out days/months/years, as the currently
// selected year/month may have changed.
setState
(()
{
});
if
(
isScrolling
)
{
return
;
}
// 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
)
{
final
DateTime
selectedDate
=
DateTime
(
selectedYear
,
selectedMonth
,
selectedDay
);
final
bool
minCheck
=
widget
.
minimumDate
?.
isAfter
(
selectedDate
)
??
false
;
final
bool
maxCheck
=
widget
.
maximumDate
?.
isBefore
(
selectedDate
)
??
false
;
if
(
minCheck
||
maxCheck
)
{
// We have minCheck === !maxCheck.
final
DateTime
targetDate
=
minCheck
?
widget
.
minimumDate
:
widget
.
maximumDate
;
_scrollToDate
(
targetDate
);
return
;
}
// Some months have less days (e.g. February). Go to the last day of that month
// if the selectedDay exceeds the maximum.
if
(
selectedDate
.
day
!=
selectedDay
)
{
final
DateTime
lastDay
=
_lastDayInMonth
(
selectedYear
,
selectedMonth
);
_scrollToDate
(
lastDay
);
}
}
void
_scrollToDate
(
DateTime
newDate
)
{
assert
(
newDate
!=
null
);
SchedulerBinding
.
instance
.
addPostFrameCallback
((
Duration
timestamp
)
{
if
(
selectedYear
!=
newDate
.
year
)
{
yearController
.
animateToItem
(
newDate
.
year
,
curve:
Curves
.
easeInOut
,
duration:
const
Duration
(
milliseconds:
200
)
,
);
}
if
(
selectedMonth
!=
newDate
.
month
)
{
monthController
.
animateToItem
(
newDate
.
month
-
1
,
curve:
Curves
.
easeInOut
,
duration:
const
Duration
(
milliseconds:
200
)
,
);
}
if
(
selectedDay
!=
newDate
.
day
)
{
dayController
.
animateToItem
(
// The next valid date is also the amount of days overflown.
dayController
.
selectedItem
-
desiredDay
,
duration:
const
Duration
(
milliseconds:
200
),
curve:
Curves
.
easeOut
,
newDate
.
day
-
1
,
curve:
Curves
.
easeInOut
,
duration:
const
Duration
(
milliseconds:
200
)
,
);
});
}
setState
(()
{
// Rebuild because the number of valid days per month are different
// depending on the month and year.
}
});
return
false
;
}
@override
...
...
@@ -1025,17 +1149,14 @@ class _CupertinoDatePickerDateState extends State<CupertinoDatePicker> {
return
MediaQuery
(
data:
const
MediaQueryData
(
textScaleFactor:
1.0
),
child:
NotificationListener
<
ScrollEndNotification
>(
onNotification:
_keepInValidRange
,
child:
DefaultTextStyle
.
merge
(
style:
_kDefaultPickerTextStyle
,
child:
CustomMultiChildLayout
(
delegate:
_DatePickerLayoutDelegate
(
columnWidths:
columnWidths
,
textDirectionFactor:
textDirectionFactor
,
),
children:
pickers
,
child:
DefaultTextStyle
.
merge
(
style:
_kDefaultPickerTextStyle
,
child:
CustomMultiChildLayout
(
delegate:
_DatePickerLayoutDelegate
(
columnWidths:
columnWidths
,
textDirectionFactor:
textDirectionFactor
,
),
children:
pickers
,
),
),
);
...
...
packages/flutter/test/cupertino/date_picker_test.dart
View file @
a9fc9e56
...
...
@@ -216,8 +216,9 @@ void main() {
);
// Distance between the first column and the last column.
final
double
distance
=
tester
.
getCenter
(
find
.
text
(
'sec.'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'12'
)).
dx
;
final
double
distance
=
tester
.
getCenter
(
find
.
text
(
'sec.'
)).
dx
-
tester
.
getCenter
(
find
.
text
(
'12'
),
).
dx
;
await
tester
.
pumpWidget
(
CupertinoApp
(
...
...
@@ -371,6 +372,28 @@ void main() {
expect
(
newDateTime
.
minute
,
6
);
});
test
(
'initial date honors minimumDate & maximumDate'
,
()
{
expect
(()
{
CupertinoDatePicker
(
onDateTimeChanged:
(
DateTime
d
)
{
},
initialDateTime:
DateTime
(
2018
,
10
,
10
),
minimumDate:
DateTime
(
2018
,
10
,
11
),
);
},
throwsAssertionError
,
);
expect
(()
{
CupertinoDatePicker
(
onDateTimeChanged:
(
DateTime
d
)
{
},
initialDateTime:
DateTime
(
2018
,
10
,
10
),
maximumDate:
DateTime
(
2018
,
10
,
9
),
);
},
throwsAssertionError
,
);
});
testWidgets
(
'changing initialDateTime after first build does not do anything'
,
(
WidgetTester
tester
)
async
{
DateTime
selectedDateTime
;
await
tester
.
pumpWidget
(
...
...
@@ -635,6 +658,73 @@ void main() {
);
});
testWidgets
(
'picker automatically scrolls away from invalid date, '
"and onDateTimeChanged doesn't report these dates"
,
(
WidgetTester
tester
)
async
{
DateTime
date
;
// 2016 is a leap year.
final
DateTime
minimum
=
DateTime
(
2016
,
2
,
29
);
final
DateTime
maximum
=
DateTime
(
2018
,
12
,
31
);
await
tester
.
pumpWidget
(
CupertinoApp
(
home:
Center
(
child:
SizedBox
(
height:
400.0
,
width:
400.0
,
child:
CupertinoDatePicker
(
mode:
CupertinoDatePickerMode
.
date
,
minimumDate:
minimum
,
maximumDate:
maximum
,
onDateTimeChanged:
(
DateTime
newDate
)
{
date
=
newDate
;
// Callback doesn't transiently go into invalid dates.
expect
(
newDate
.
isAtSameMomentAs
(
minimum
)
||
newDate
.
isAfter
(
minimum
),
isTrue
);
expect
(
newDate
.
isAtSameMomentAs
(
maximum
)
||
newDate
.
isBefore
(
maximum
),
isTrue
);
},
initialDateTime:
DateTime
(
2017
,
2
,
28
),
),
),
),
),
);
// 2017 has 28 days in Feb so 29 is greyed out.
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'29'
)).
style
.
color
,
isSameColorAs
(
CupertinoColors
.
inactiveGray
),
);
await
tester
.
drag
(
find
.
text
(
'2017'
),
const
Offset
(
0.0
,
32.0
),
touchSlopY:
0.0
);
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
// Now the autoscrolling should happen.
expect
(
date
,
DateTime
(
2016
,
2
,
29
),
);
// 2016 has 29 days in Feb so 29 is not greyed out.
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'29'
)).
style
.
color
,
isNot
(
isSameColorAs
(
CupertinoColors
.
inactiveGray
)),
);
await
tester
.
drag
(
find
.
text
(
'2016'
),
const
Offset
(
0.0
,
-
32.0
),
touchSlopY:
0.0
);
await
tester
.
pump
();
// Once to trigger the post frame animate call.
await
tester
.
pumpAndSettle
();
expect
(
date
,
DateTime
(
2017
,
2
,
28
),
);
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'29'
)).
style
.
color
,
isSameColorAs
(
CupertinoColors
.
inactiveGray
),
);
});
testWidgets
(
'picker automatically scrolls away from invalid date on day change'
,
(
WidgetTester
tester
)
async
{
DateTime
date
;
await
tester
.
pumpWidget
(
...
...
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