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
cc36608f
Unverified
Commit
cc36608f
authored
Jun 23, 2020
by
Darren Austin
Committed by
GitHub
Jun 23, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Keyboard navigation for the Material Date Picker grid (#59586)
parent
dd6dd7ae
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
492 additions
and
114 deletions
+492
-114
calendar_date_picker.dart
...lutter/lib/src/material/pickers/calendar_date_picker.dart
+289
-90
date_picker_dialog.dart
.../flutter/lib/src/material/pickers/date_picker_dialog.dart
+27
-19
date_utils.dart
packages/flutter/lib/src/material/pickers/date_utils.dart
+12
-4
date_picker_test.dart
packages/flutter/test/material/date_picker_test.dart
+164
-1
No files found.
packages/flutter/lib/src/material/pickers/calendar_date_picker.dart
View file @
cc36608f
...
@@ -283,12 +283,10 @@ class _CalendarDatePickerState extends State<CalendarDatePicker> {
...
@@ -283,12 +283,10 @@ class _CalendarDatePickerState extends State<CalendarDatePicker> {
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
Stack
(
return
Stack
(
children:
<
Widget
>[
children:
<
Widget
>[
SingleChildScrollView
(
SizedBox
(
child:
SizedBox
(
height:
_subHeaderHeight
+
_maxDayPickerHeight
,
height:
_maxDayPickerHeight
,
child:
_buildPicker
(),
child:
_buildPicker
(),
),
),
),
// Put the mode toggle button on top so that it won't be covered up by the _MonthPicker
// Put the mode toggle button on top so that it won't be covered up by the _MonthPicker
_DatePickerModeToggleButton
(
_DatePickerModeToggleButton
(
mode:
_mode
,
mode:
_mode
,
...
@@ -476,12 +474,17 @@ class _MonthPicker extends StatefulWidget {
...
@@ -476,12 +474,17 @@ class _MonthPicker extends StatefulWidget {
}
}
class
_MonthPickerState
extends
State
<
_MonthPicker
>
{
class
_MonthPickerState
extends
State
<
_MonthPicker
>
{
final
GlobalKey
_pageViewKey
=
GlobalKey
();
DateTime
_currentMonth
;
DateTime
_currentMonth
;
DateTime
_nextMonthDate
;
DateTime
_nextMonthDate
;
DateTime
_previousMonthDate
;
DateTime
_previousMonthDate
;
PageController
_pageController
;
PageController
_pageController
;
MaterialLocalizations
_localizations
;
MaterialLocalizations
_localizations
;
TextDirection
_textDirection
;
TextDirection
_textDirection
;
Map
<
LogicalKeySet
,
Intent
>
_shortcutMap
;
Map
<
Type
,
Action
<
Intent
>>
_actionMap
;
FocusNode
_dayGridFocus
;
DateTime
_focusedDay
;
@override
@override
void
initState
()
{
void
initState
()
{
...
@@ -490,6 +493,18 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -490,6 +493,18 @@ class _MonthPickerState extends State<_MonthPicker> {
_previousMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
-
1
);
_previousMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
-
1
);
_nextMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
1
);
_nextMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
1
);
_pageController
=
PageController
(
initialPage:
utils
.
monthDelta
(
widget
.
firstDate
,
_currentMonth
));
_pageController
=
PageController
(
initialPage:
utils
.
monthDelta
(
widget
.
firstDate
,
_currentMonth
));
_shortcutMap
=
<
LogicalKeySet
,
Intent
>{
LogicalKeySet
(
LogicalKeyboardKey
.
arrowLeft
):
const
DirectionalFocusIntent
(
TraversalDirection
.
left
),
LogicalKeySet
(
LogicalKeyboardKey
.
arrowRight
):
const
DirectionalFocusIntent
(
TraversalDirection
.
right
),
LogicalKeySet
(
LogicalKeyboardKey
.
arrowDown
):
const
DirectionalFocusIntent
(
TraversalDirection
.
down
),
LogicalKeySet
(
LogicalKeyboardKey
.
arrowUp
):
const
DirectionalFocusIntent
(
TraversalDirection
.
up
),
};
_actionMap
=
<
Type
,
Action
<
Intent
>>{
NextFocusIntent:
CallbackAction
<
NextFocusIntent
>(
onInvoke:
_handleGridNextFocus
),
PreviousFocusIntent:
CallbackAction
<
PreviousFocusIntent
>(
onInvoke:
_handleGridPreviousFocus
),
DirectionalFocusIntent:
CallbackAction
<
DirectionalFocusIntent
>(
onInvoke:
_handleDirectionFocus
),
};
_dayGridFocus
=
FocusNode
(
debugLabel:
'Day Grid'
);
}
}
@override
@override
...
@@ -502,19 +517,58 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -502,19 +517,58 @@ class _MonthPickerState extends State<_MonthPicker> {
@override
@override
void
dispose
()
{
void
dispose
()
{
_pageController
?.
dispose
();
_pageController
?.
dispose
();
_dayGridFocus
.
dispose
();
super
.
dispose
();
super
.
dispose
();
}
}
void
_handleDateSelected
(
DateTime
selectedDate
)
{
_focusedDay
=
selectedDate
;
widget
.
onChanged
?.
call
(
selectedDate
);
}
void
_handleMonthPageChanged
(
int
monthPage
)
{
void
_handleMonthPageChanged
(
int
monthPage
)
{
setState
(()
{
final
DateTime
monthDate
=
utils
.
addMonthsToMonthDate
(
widget
.
firstDate
,
monthPage
);
final
DateTime
monthDate
=
utils
.
addMonthsToMonthDate
(
widget
.
firstDate
,
monthPage
);
if
(
_currentMonth
.
year
!=
monthDate
.
year
||
_currentMonth
.
month
!=
monthDate
.
month
)
{
if
(!
utils
.
isSameMonth
(
_currentMonth
,
monthDate
)
)
{
_currentMonth
=
DateTime
(
monthDate
.
year
,
monthDate
.
month
);
_currentMonth
=
DateTime
(
monthDate
.
year
,
monthDate
.
month
);
_previousMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
-
1
);
_previousMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
-
1
);
_nextMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
1
);
_nextMonthDate
=
utils
.
addMonthsToMonthDate
(
_currentMonth
,
1
);
widget
.
onDisplayedMonthChanged
?.
call
(
_currentMonth
);
widget
.
onDisplayedMonthChanged
?.
call
(
_currentMonth
);
if
(
_focusedDay
!=
null
&&
!
utils
.
isSameMonth
(
_focusedDay
,
_currentMonth
))
{
// We have navigated to a new month with the grid focused, but the
// focused day is not in this month. Choose a new one trying to keep
// the same day of the month.
_focusedDay
=
_focusableDayForMonth
(
_currentMonth
,
_focusedDay
.
day
);
}
}
}
});
}
}
/// Returns a focusable date for the given month.
///
/// If the preferredDay is available in the month it will be returned,
/// otherwise the first selectable day in the month will be returned. If
/// no dates are selectable in the month, then it will return null.
DateTime
_focusableDayForMonth
(
DateTime
month
,
int
preferredDay
)
{
final
int
daysInMonth
=
utils
.
getDaysInMonth
(
month
.
year
,
month
.
month
);
// Can we use the preferred day in this month?
if
(
preferredDay
<=
daysInMonth
)
{
final
DateTime
newFocus
=
DateTime
(
month
.
year
,
month
.
month
,
preferredDay
);
if
(
_isSelectable
(
newFocus
))
return
newFocus
;
}
// Start at the 1st and take the first selectable date.
for
(
int
day
=
1
;
day
<=
daysInMonth
;
day
++)
{
final
DateTime
newFocus
=
DateTime
(
month
.
year
,
month
.
month
,
day
);
if
(
_isSelectable
(
newFocus
))
return
newFocus
;
}
return
null
;
}
/// Navigate to the next month.
void
_handleNextMonth
()
{
void
_handleNextMonth
()
{
if
(!
_isDisplayingLastMonth
)
{
if
(!
_isDisplayingLastMonth
)
{
SemanticsService
.
announce
(
SemanticsService
.
announce
(
...
@@ -528,6 +582,7 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -528,6 +582,7 @@ class _MonthPickerState extends State<_MonthPicker> {
}
}
}
}
/// Navigate to the previous month.
void
_handlePreviousMonth
()
{
void
_handlePreviousMonth
()
{
if
(!
_isDisplayingFirstMonth
)
{
if
(!
_isDisplayingFirstMonth
)
{
SemanticsService
.
announce
(
SemanticsService
.
announce
(
...
@@ -541,6 +596,15 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -541,6 +596,15 @@ class _MonthPickerState extends State<_MonthPicker> {
}
}
}
}
/// Navigate to the given month.
void
_showMonth
(
DateTime
month
)
{
final
int
monthPage
=
utils
.
monthDelta
(
widget
.
firstDate
,
month
);
_pageController
.
animateToPage
(
monthPage
,
duration:
_monthScrollDuration
,
curve:
Curves
.
ease
);
}
/// True if the earliest allowable month is displayed.
/// True if the earliest allowable month is displayed.
bool
get
_isDisplayingFirstMonth
{
bool
get
_isDisplayingFirstMonth
{
return
!
_currentMonth
.
isAfter
(
return
!
_currentMonth
.
isAfter
(
...
@@ -555,13 +619,96 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -555,13 +619,96 @@ class _MonthPickerState extends State<_MonthPicker> {
);
);
}
}
/// Handler for when the overall day grid obtains or loses focus.
void
_handleGridFocusChange
(
bool
focused
)
{
setState
(()
{
if
(
focused
&&
_focusedDay
==
null
)
{
if
(
utils
.
isSameMonth
(
widget
.
selectedDate
,
_currentMonth
))
{
_focusedDay
=
widget
.
selectedDate
;
}
else
if
(
utils
.
isSameMonth
(
widget
.
currentDate
,
_currentMonth
))
{
_focusedDay
=
_focusableDayForMonth
(
_currentMonth
,
widget
.
currentDate
.
day
);
}
else
{
_focusedDay
=
_focusableDayForMonth
(
_currentMonth
,
1
);
}
}
});
}
/// Move focus to the next element after the day grid.
void
_handleGridNextFocus
(
NextFocusIntent
intent
)
{
_dayGridFocus
.
requestFocus
();
_dayGridFocus
.
nextFocus
();
}
/// Move focus to the previous element before the day grid.
void
_handleGridPreviousFocus
(
PreviousFocusIntent
intent
)
{
_dayGridFocus
.
requestFocus
();
_dayGridFocus
.
previousFocus
();
}
/// Move the internal focus date in the direction of the given intent.
///
/// This will attempt to move the focused day to the next selectable day in
/// the given direction. If the new date is not in the current month, then
/// the page view will be scrolled to show the new date's month.
///
/// For horizontal directions, it will move forward or backward a day (depending
/// on the current [TextDirection]). For vertical directions it will move up and
/// down a week at a time.
void
_handleDirectionFocus
(
DirectionalFocusIntent
intent
)
{
assert
(
_focusedDay
!=
null
);
setState
(()
{
final
DateTime
nextDate
=
_nextDateInDirection
(
_focusedDay
,
intent
.
direction
);
if
(
nextDate
!=
null
)
{
_focusedDay
=
nextDate
;
if
(!
utils
.
isSameMonth
(
_focusedDay
,
_currentMonth
))
{
_showMonth
(
_focusedDay
);
}
}
});
}
static
const
Map
<
TraversalDirection
,
Duration
>
_directionOffset
=
<
TraversalDirection
,
Duration
>{
TraversalDirection
.
up
:
Duration
(
days:
-
DateTime
.
daysPerWeek
),
TraversalDirection
.
right
:
Duration
(
days:
1
),
TraversalDirection
.
down
:
Duration
(
days:
DateTime
.
daysPerWeek
),
TraversalDirection
.
left
:
Duration
(
days:
-
1
),
};
Duration
_dayDirectionOffset
(
TraversalDirection
traversalDirection
,
TextDirection
textDirection
)
{
// Swap left and right if the text direction if RTL
if
(
textDirection
==
TextDirection
.
rtl
)
{
if
(
traversalDirection
==
TraversalDirection
.
left
)
traversalDirection
=
TraversalDirection
.
right
;
else
if
(
traversalDirection
==
TraversalDirection
.
right
)
traversalDirection
=
TraversalDirection
.
left
;
}
return
_directionOffset
[
traversalDirection
];
}
DateTime
_nextDateInDirection
(
DateTime
date
,
TraversalDirection
direction
)
{
final
TextDirection
textDirection
=
Directionality
.
of
(
context
);
DateTime
nextDate
=
date
.
toUtc
().
add
(
_dayDirectionOffset
(
direction
,
textDirection
));
while
(!
nextDate
.
isBefore
(
widget
.
firstDate
)
&&
!
nextDate
.
isAfter
(
widget
.
lastDate
))
{
if
(
_isSelectable
(
nextDate
))
{
return
nextDate
;
}
nextDate
=
nextDate
.
add
(
_dayDirectionOffset
(
direction
,
textDirection
));
}
return
null
;
}
bool
_isSelectable
(
DateTime
date
)
{
return
widget
.
selectableDayPredicate
==
null
||
widget
.
selectableDayPredicate
.
call
(
date
);
}
Widget
_buildItems
(
BuildContext
context
,
int
index
)
{
Widget
_buildItems
(
BuildContext
context
,
int
index
)
{
final
DateTime
month
=
utils
.
addMonthsToMonthDate
(
widget
.
firstDate
,
index
);
final
DateTime
month
=
utils
.
addMonthsToMonthDate
(
widget
.
firstDate
,
index
);
return
_DayPicker
(
return
_DayPicker
(
key:
ValueKey
<
DateTime
>(
month
),
key:
ValueKey
<
DateTime
>(
month
),
selectedDate:
widget
.
selectedDate
,
selectedDate:
widget
.
selectedDate
,
currentDate:
widget
.
currentDate
,
currentDate:
widget
.
currentDate
,
onChanged:
widget
.
onChang
ed
,
onChanged:
_handleDateSelect
ed
,
firstDate:
widget
.
firstDate
,
firstDate:
widget
.
firstDate
,
lastDate:
widget
.
lastDate
,
lastDate:
widget
.
lastDate
,
displayedMonth:
month
,
displayedMonth:
month
,
...
@@ -599,9 +746,18 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -599,9 +746,18 @@ class _MonthPickerState extends State<_MonthPicker> {
],
],
),
),
),
),
_DayHeaders
(),
Expanded
(
Expanded
(
child:
FocusableActionDetector
(
shortcuts:
_shortcutMap
,
actions:
_actionMap
,
focusNode:
_dayGridFocus
,
onFocusChange:
_handleGridFocusChange
,
child:
_FocusedDate
(
date:
_dayGridFocus
.
hasFocus
?
_focusedDay
:
null
,
child:
Container
(
color:
_dayGridFocus
.
hasFocus
?
Theme
.
of
(
context
).
focusColor
:
null
,
child:
PageView
.
builder
(
child:
PageView
.
builder
(
key:
_pageViewKey
,
controller:
_pageController
,
controller:
_pageController
,
itemBuilder:
_buildItems
,
itemBuilder:
_buildItems
,
itemCount:
utils
.
monthDelta
(
widget
.
firstDate
,
widget
.
lastDate
)
+
1
,
itemCount:
utils
.
monthDelta
(
widget
.
firstDate
,
widget
.
lastDate
)
+
1
,
...
@@ -609,17 +765,44 @@ class _MonthPickerState extends State<_MonthPicker> {
...
@@ -609,17 +765,44 @@ class _MonthPickerState extends State<_MonthPicker> {
onPageChanged:
_handleMonthPageChanged
,
onPageChanged:
_handleMonthPageChanged
,
),
),
),
),
),
),
),
],
],
),
),
);
);
}
}
}
}
/// InheritedWidget indicating what the current focused date is for its children.
///
/// This is used by the [_MonthPicker] to let its children [_DayPicker]s know
/// what the currently focused date (if any) should be.
class
_FocusedDate
extends
InheritedWidget
{
const
_FocusedDate
({
Key
key
,
Widget
child
,
this
.
date
})
:
super
(
key:
key
,
child:
child
);
final
DateTime
date
;
@override
bool
updateShouldNotify
(
_FocusedDate
oldWidget
)
{
return
!
utils
.
isSameDay
(
date
,
oldWidget
.
date
);
}
static
DateTime
of
(
BuildContext
context
)
{
final
_FocusedDate
focusedDate
=
context
.
dependOnInheritedWidgetOfExactType
<
_FocusedDate
>();
return
focusedDate
?.
date
;
}
}
/// Displays the days of a given month and allows choosing a day.
/// Displays the days of a given month and allows choosing a day.
///
///
/// The days are arranged in a rectangular grid with one column for each day of
/// The days are arranged in a rectangular grid with one column for each day of
/// the week.
/// the week.
class
_DayPicker
extends
State
less
Widget
{
class
_DayPicker
extends
State
ful
Widget
{
/// Creates a day picker.
/// Creates a day picker.
_DayPicker
({
_DayPicker
({
Key
key
,
Key
key
,
...
@@ -668,11 +851,82 @@ class _DayPicker extends StatelessWidget {
...
@@ -668,11 +851,82 @@ class _DayPicker extends StatelessWidget {
/// Optional user supplied predicate function to customize selectable days.
/// Optional user supplied predicate function to customize selectable days.
final
SelectableDayPredicate
selectableDayPredicate
;
final
SelectableDayPredicate
selectableDayPredicate
;
@override
_DayPickerState
createState
()
=>
_DayPickerState
();
}
class
_DayPickerState
extends
State
<
_DayPicker
>
{
/// List of [FocusNode]s, one for each day of the month.
List
<
FocusNode
>
_dayFocusNodes
;
@override
void
initState
()
{
super
.
initState
();
final
int
daysInMonth
=
utils
.
getDaysInMonth
(
widget
.
displayedMonth
.
year
,
widget
.
displayedMonth
.
month
);
_dayFocusNodes
=
List
<
FocusNode
>.
generate
(
daysInMonth
,
(
int
index
)
=>
FocusNode
(
skipTraversal:
true
,
debugLabel:
'Day
${index + 1}
'
)
);
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
// Check to see if the focused date is in this month, if so focus it.
final
DateTime
focusedDate
=
_FocusedDate
.
of
(
context
);
if
(
focusedDate
!=
null
&&
utils
.
isSameMonth
(
widget
.
displayedMonth
,
focusedDate
))
{
_dayFocusNodes
[
focusedDate
.
day
-
1
].
requestFocus
();
}
}
@override
void
dispose
()
{
for
(
final
FocusNode
node
in
_dayFocusNodes
)
{
node
.
dispose
();
}
super
.
dispose
();
}
/// Builds widgets showing abbreviated days of week. The first widget in the
/// returned list corresponds to the first day of week for the current locale.
///
/// Examples:
///
/// ```
/// ┌ Sunday is the first day of week in the US (en_US)
/// |
/// S M T W T F S <-- the returned list contains these widgets
/// _ _ _ _ _ 1 2
/// 3 4 5 6 7 8 9
///
/// ┌ But it's Monday in the UK (en_GB)
/// |
/// M T W T F S S <-- the returned list contains these widgets
/// _ _ _ _ 1 2 3
/// 4 5 6 7 8 9 10
/// ```
List
<
Widget
>
_dayHeaders
(
TextStyle
headerStyle
,
MaterialLocalizations
localizations
)
{
final
List
<
Widget
>
result
=
<
Widget
>[];
for
(
int
i
=
localizations
.
firstDayOfWeekIndex
;
true
;
i
=
(
i
+
1
)
%
7
)
{
final
String
weekday
=
localizations
.
narrowWeekdays
[
i
];
result
.
add
(
ExcludeSemantics
(
child:
Center
(
child:
Text
(
weekday
,
style:
headerStyle
)),
));
if
(
i
==
(
localizations
.
firstDayOfWeekIndex
-
1
)
%
7
)
break
;
}
return
result
;
}
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
ColorScheme
colorScheme
=
Theme
.
of
(
context
).
colorScheme
;
final
ColorScheme
colorScheme
=
Theme
.
of
(
context
).
colorScheme
;
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
TextTheme
textTheme
=
Theme
.
of
(
context
).
textTheme
;
final
TextTheme
textTheme
=
Theme
.
of
(
context
).
textTheme
;
final
TextStyle
headerStyle
=
textTheme
.
caption
?.
apply
(
color:
colorScheme
.
onSurface
.
withOpacity
(
0.60
),
);
final
TextStyle
dayStyle
=
textTheme
.
caption
;
final
TextStyle
dayStyle
=
textTheme
.
caption
;
final
Color
enabledDayColor
=
colorScheme
.
onSurface
.
withOpacity
(
0.87
);
final
Color
enabledDayColor
=
colorScheme
.
onSurface
.
withOpacity
(
0.87
);
final
Color
disabledDayColor
=
colorScheme
.
onSurface
.
withOpacity
(
0.38
);
final
Color
disabledDayColor
=
colorScheme
.
onSurface
.
withOpacity
(
0.38
);
...
@@ -680,13 +934,13 @@ class _DayPicker extends StatelessWidget {
...
@@ -680,13 +934,13 @@ class _DayPicker extends StatelessWidget {
final
Color
selectedDayBackground
=
colorScheme
.
primary
;
final
Color
selectedDayBackground
=
colorScheme
.
primary
;
final
Color
todayColor
=
colorScheme
.
primary
;
final
Color
todayColor
=
colorScheme
.
primary
;
final
int
year
=
displayedMonth
.
year
;
final
int
year
=
widget
.
displayedMonth
.
year
;
final
int
month
=
displayedMonth
.
month
;
final
int
month
=
widget
.
displayedMonth
.
month
;
final
int
daysInMonth
=
utils
.
getDaysInMonth
(
year
,
month
);
final
int
daysInMonth
=
utils
.
getDaysInMonth
(
year
,
month
);
final
int
dayOffset
=
utils
.
firstDayOffset
(
year
,
month
,
localizations
);
final
int
dayOffset
=
utils
.
firstDayOffset
(
year
,
month
,
localizations
);
final
List
<
Widget
>
dayItems
=
<
Widget
>[]
;
final
List
<
Widget
>
dayItems
=
_dayHeaders
(
headerStyle
,
localizations
)
;
// 1-based day of month, e.g. 1-31 for January, and 1-29 for February on
// 1-based day of month, e.g. 1-31 for January, and 1-29 for February on
// a leap year.
// a leap year.
int
day
=
-
dayOffset
;
int
day
=
-
dayOffset
;
...
@@ -696,13 +950,14 @@ class _DayPicker extends StatelessWidget {
...
@@ -696,13 +950,14 @@ class _DayPicker extends StatelessWidget {
dayItems
.
add
(
Container
());
dayItems
.
add
(
Container
());
}
else
{
}
else
{
final
DateTime
dayToBuild
=
DateTime
(
year
,
month
,
day
);
final
DateTime
dayToBuild
=
DateTime
(
year
,
month
,
day
);
final
bool
isDisabled
=
dayToBuild
.
isAfter
(
lastDate
)
||
final
bool
isDisabled
=
dayToBuild
.
isAfter
(
widget
.
lastDate
)
||
dayToBuild
.
isBefore
(
firstDate
)
||
dayToBuild
.
isBefore
(
widget
.
firstDate
)
||
(
selectableDayPredicate
!=
null
&&
!
selectableDayPredicate
(
dayToBuild
));
(
widget
.
selectableDayPredicate
!=
null
&&
!
widget
.
selectableDayPredicate
(
dayToBuild
));
final
bool
isSelectedDay
=
utils
.
isSameDay
(
widget
.
selectedDate
,
dayToBuild
);
final
bool
isToday
=
utils
.
isSameDay
(
widget
.
currentDate
,
dayToBuild
);
BoxDecoration
decoration
;
BoxDecoration
decoration
;
Color
dayColor
=
enabledDayColor
;
Color
dayColor
=
enabledDayColor
;
final
bool
isSelectedDay
=
utils
.
isSameDay
(
selectedDate
,
dayToBuild
);
if
(
isSelectedDay
)
{
if
(
isSelectedDay
)
{
// The selected day gets a circle background highlight, and a
// The selected day gets a circle background highlight, and a
// contrasting text color.
// contrasting text color.
...
@@ -713,7 +968,7 @@ class _DayPicker extends StatelessWidget {
...
@@ -713,7 +968,7 @@ class _DayPicker extends StatelessWidget {
);
);
}
else
if
(
isDisabled
)
{
}
else
if
(
isDisabled
)
{
dayColor
=
disabledDayColor
;
dayColor
=
disabledDayColor
;
}
else
if
(
utils
.
isSameDay
(
currentDate
,
dayToBuild
)
)
{
}
else
if
(
isToday
)
{
// The current day gets a different text color and a circle stroke
// The current day gets a different text color and a circle stroke
// border.
// border.
dayColor
=
todayColor
;
dayColor
=
todayColor
;
...
@@ -735,9 +990,11 @@ class _DayPicker extends StatelessWidget {
...
@@ -735,9 +990,11 @@ class _DayPicker extends StatelessWidget {
child:
dayWidget
,
child:
dayWidget
,
);
);
}
else
{
}
else
{
dayWidget
=
GestureDetector
(
dayWidget
=
InkResponse
(
behavior:
HitTestBehavior
.
opaque
,
focusNode:
_dayFocusNodes
[
day
-
1
],
onTap:
()
=>
onChanged
(
dayToBuild
),
onTap:
()
=>
widget
.
onChanged
(
dayToBuild
),
radius:
_dayPickerRowHeight
/
2
+
4
,
splashColor:
selectedDayBackground
.
withOpacity
(
0.38
),
child:
Semantics
(
child:
Semantics
(
// We want the day of month to be spoken first irrespective of the
// We want the day of month to be spoken first irrespective of the
// locale-specific preferences or TextDirection. This is because
// locale-specific preferences or TextDirection. This is because
...
@@ -781,7 +1038,7 @@ class _DayPickerGridDelegate extends SliverGridDelegate {
...
@@ -781,7 +1038,7 @@ class _DayPickerGridDelegate extends SliverGridDelegate {
const
int
columnCount
=
DateTime
.
daysPerWeek
;
const
int
columnCount
=
DateTime
.
daysPerWeek
;
final
double
tileWidth
=
constraints
.
crossAxisExtent
/
columnCount
;
final
double
tileWidth
=
constraints
.
crossAxisExtent
/
columnCount
;
final
double
tileHeight
=
math
.
min
(
_dayPickerRowHeight
,
final
double
tileHeight
=
math
.
min
(
_dayPickerRowHeight
,
constraints
.
viewportMainAxisExtent
/
_maxDayPickerRowCount
);
constraints
.
viewportMainAxisExtent
/
(
_maxDayPickerRowCount
+
1
)
);
return
SliverGridRegularTileLayout
(
return
SliverGridRegularTileLayout
(
childCrossAxisExtent:
tileWidth
,
childCrossAxisExtent:
tileWidth
,
childMainAxisExtent:
tileHeight
,
childMainAxisExtent:
tileHeight
,
...
@@ -798,64 +1055,6 @@ class _DayPickerGridDelegate extends SliverGridDelegate {
...
@@ -798,64 +1055,6 @@ class _DayPickerGridDelegate extends SliverGridDelegate {
const
_DayPickerGridDelegate
_dayPickerGridDelegate
=
_DayPickerGridDelegate
();
const
_DayPickerGridDelegate
_dayPickerGridDelegate
=
_DayPickerGridDelegate
();
class
_DayHeaders
extends
StatelessWidget
{
/// Builds widgets showing abbreviated days of week. The first widget in the
/// returned list corresponds to the first day of week for the current locale.
///
/// Examples:
///
/// ```
/// ┌ Sunday is the first day of week in the US (en_US)
/// |
/// S M T W T F S <-- the returned list contains these widgets
/// _ _ _ _ _ 1 2
/// 3 4 5 6 7 8 9
///
/// ┌ But it's Monday in the UK (en_GB)
/// |
/// M T W T F S S <-- the returned list contains these widgets
/// _ _ _ _ 1 2 3
/// 4 5 6 7 8 9 10
/// ```
List
<
Widget
>
_getDayHeaders
(
TextStyle
headerStyle
,
MaterialLocalizations
localizations
)
{
final
List
<
Widget
>
result
=
<
Widget
>[];
for
(
int
i
=
localizations
.
firstDayOfWeekIndex
;
true
;
i
=
(
i
+
1
)
%
7
)
{
final
String
weekday
=
localizations
.
narrowWeekdays
[
i
];
result
.
add
(
ExcludeSemantics
(
child:
Center
(
child:
Text
(
weekday
,
style:
headerStyle
)),
));
if
(
i
==
(
localizations
.
firstDayOfWeekIndex
-
1
)
%
7
)
break
;
}
return
result
;
}
@override
Widget
build
(
BuildContext
context
)
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
ColorScheme
colorScheme
=
theme
.
colorScheme
;
final
TextStyle
dayHeaderStyle
=
theme
.
textTheme
.
caption
?.
apply
(
color:
colorScheme
.
onSurface
.
withOpacity
(
0.60
),
);
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
List
<
Widget
>
labels
=
_getDayHeaders
(
dayHeaderStyle
,
localizations
);
return
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
_monthPickerHorizontalPadding
,
),
child:
GridView
.
custom
(
shrinkWrap:
true
,
gridDelegate:
_dayPickerGridDelegate
,
childrenDelegate:
SliverChildListDelegate
(
labels
,
addRepaintBoundaries:
false
,
),
),
);
}
}
/// A scrollable list of years to allow picking a year.
/// A scrollable list of years to allow picking a year.
class
_YearPicker
extends
StatefulWidget
{
class
_YearPicker
extends
StatefulWidget
{
/// Creates a year picker.
/// Creates a year picker.
...
...
packages/flutter/lib/src/material/pickers/date_picker_dialog.dart
View file @
cc36608f
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
import
'dart:math'
as
math
;
import
'dart:math'
as
math
;
import
'package:flutter/
foundation
.dart'
;
import
'package:flutter/
services
.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
import
'../button_bar.dart'
;
import
'../button_bar.dart'
;
...
@@ -357,6 +357,11 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -357,6 +357,11 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
return
null
;
return
null
;
}
}
static
final
Map
<
LogicalKeySet
,
Intent
>
_formShortcutMap
=
<
LogicalKeySet
,
Intent
>{
// Pressing enter on the field will move focus to the next field or control.
LogicalKeySet
(
LogicalKeyboardKey
.
enter
):
const
NextFocusIntent
(),
};
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
ThemeData
theme
=
Theme
.
of
(
context
);
...
@@ -419,6 +424,8 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -419,6 +424,8 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
child:
Container
(
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
24
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
24
),
height:
orientation
==
Orientation
.
portrait
?
_inputFormPortraitHeight
:
_inputFormLandscapeHeight
,
height:
orientation
==
Orientation
.
portrait
?
_inputFormPortraitHeight
:
_inputFormLandscapeHeight
,
child:
Shortcuts
(
shortcuts:
_formShortcutMap
,
child:
Column
(
child:
Column
(
children:
<
Widget
>[
children:
<
Widget
>[
const
Spacer
(),
const
Spacer
(),
...
@@ -439,6 +446,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
...
@@ -439,6 +446,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
],
],
),
),
),
),
),
);
);
entryModeIcon
=
Icons
.
calendar_today
;
entryModeIcon
=
Icons
.
calendar_today
;
entryModeTooltip
=
localizations
.
calendarModeButtonLabel
;
entryModeTooltip
=
localizations
.
calendarModeButtonLabel
;
...
...
packages/flutter/lib/src/material/pickers/date_utils.dart
View file @
cc36608f
...
@@ -26,12 +26,20 @@ DateTimeRange datesOnly(DateTimeRange range) {
...
@@ -26,12 +26,20 @@ DateTimeRange datesOnly(DateTimeRange range) {
}
}
/// Returns true if the two [DateTime] objects have the same day, month, and
/// Returns true if the two [DateTime] objects have the same day, month, and
/// year.
/// year
, or are both null
.
bool
isSameDay
(
DateTime
dateA
,
DateTime
dateB
)
{
bool
isSameDay
(
DateTime
dateA
,
DateTime
dateB
)
{
return
return
dateA
.
year
==
dateB
.
year
&&
dateA
?.
year
==
dateB
?.
year
&&
dateA
.
month
==
dateB
.
month
&&
dateA
?.
month
==
dateB
?.
month
&&
dateA
.
day
==
dateB
.
day
;
dateA
?.
day
==
dateB
?.
day
;
}
/// Returns true if the two [DateTime] objects have the same month, and
/// year, or are both null.
bool
isSameMonth
(
DateTime
dateA
,
DateTime
dateB
)
{
return
dateA
?.
year
==
dateB
?.
year
&&
dateA
?.
month
==
dateB
?.
month
;
}
}
/// Determines the number of months between two [DateTime] objects.
/// Determines the number of months between two [DateTime] objects.
...
...
packages/flutter/test/material/date_picker_test.dart
View file @
cc36608f
...
@@ -83,7 +83,11 @@ void main() {
...
@@ -83,7 +83,11 @@ void main() {
SystemChannels
.
platform
.
setMockMethodCallHandler
(
null
);
SystemChannels
.
platform
.
setMockMethodCallHandler
(
null
);
});
});
Future
<
void
>
prepareDatePicker
(
WidgetTester
tester
,
Future
<
void
>
callback
(
Future
<
DateTime
>
date
))
async
{
Future
<
void
>
prepareDatePicker
(
WidgetTester
tester
,
Future
<
void
>
callback
(
Future
<
DateTime
>
date
),
{
TextDirection
textDirection
=
TextDirection
.
ltr
}
)
async
{
BuildContext
buttonContext
;
BuildContext
buttonContext
;
await
tester
.
pumpWidget
(
MaterialApp
(
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Material
(
home:
Material
(
...
@@ -119,6 +123,12 @@ void main() {
...
@@ -119,6 +123,12 @@ void main() {
fieldHintText:
fieldHintText
,
fieldHintText:
fieldHintText
,
fieldLabelText:
fieldLabelText
,
fieldLabelText:
fieldLabelText
,
helpText:
helpText
,
helpText:
helpText
,
builder:
(
BuildContext
context
,
Widget
child
)
{
return
Directionality
(
textDirection:
textDirection
,
child:
child
,
);
},
);
);
await
tester
.
pumpAndSettle
(
const
Duration
(
seconds:
1
));
await
tester
.
pumpAndSettle
(
const
Duration
(
seconds:
1
));
...
@@ -845,123 +855,153 @@ void main() {
...
@@ -845,123 +855,153 @@ void main() {
expect
(
tester
.
getSemantics
(
find
.
text
(
'1'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'1'
)),
matchesSemantics
(
label:
'1, Friday, January 1, 2016'
,
label:
'1, Friday, January 1, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'2'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'2'
)),
matchesSemantics
(
label:
'2, Saturday, January 2, 2016'
,
label:
'2, Saturday, January 2, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'3'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'3'
)),
matchesSemantics
(
label:
'3, Sunday, January 3, 2016'
,
label:
'3, Sunday, January 3, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'4'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'4'
)),
matchesSemantics
(
label:
'4, Monday, January 4, 2016'
,
label:
'4, Monday, January 4, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'5'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'5'
)),
matchesSemantics
(
label:
'5, Tuesday, January 5, 2016'
,
label:
'5, Tuesday, January 5, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'6'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'6'
)),
matchesSemantics
(
label:
'6, Wednesday, January 6, 2016'
,
label:
'6, Wednesday, January 6, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'7'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'7'
)),
matchesSemantics
(
label:
'7, Thursday, January 7, 2016'
,
label:
'7, Thursday, January 7, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'8'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'8'
)),
matchesSemantics
(
label:
'8, Friday, January 8, 2016'
,
label:
'8, Friday, January 8, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'9'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'9'
)),
matchesSemantics
(
label:
'9, Saturday, January 9, 2016'
,
label:
'9, Saturday, January 9, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'10'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'10'
)),
matchesSemantics
(
label:
'10, Sunday, January 10, 2016'
,
label:
'10, Sunday, January 10, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'11'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'11'
)),
matchesSemantics
(
label:
'11, Monday, January 11, 2016'
,
label:
'11, Monday, January 11, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'12'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'12'
)),
matchesSemantics
(
label:
'12, Tuesday, January 12, 2016'
,
label:
'12, Tuesday, January 12, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'13'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'13'
)),
matchesSemantics
(
label:
'13, Wednesday, January 13, 2016'
,
label:
'13, Wednesday, January 13, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'14'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'14'
)),
matchesSemantics
(
label:
'14, Thursday, January 14, 2016'
,
label:
'14, Thursday, January 14, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'15'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'15'
)),
matchesSemantics
(
label:
'15, Friday, January 15, 2016'
,
label:
'15, Friday, January 15, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isSelected:
true
,
isSelected:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'16'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'16'
)),
matchesSemantics
(
label:
'16, Saturday, January 16, 2016'
,
label:
'16, Saturday, January 16, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'17'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'17'
)),
matchesSemantics
(
label:
'17, Sunday, January 17, 2016'
,
label:
'17, Sunday, January 17, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'18'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'18'
)),
matchesSemantics
(
label:
'18, Monday, January 18, 2016'
,
label:
'18, Monday, January 18, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'19'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'19'
)),
matchesSemantics
(
label:
'19, Tuesday, January 19, 2016'
,
label:
'19, Tuesday, January 19, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'20'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'20'
)),
matchesSemantics
(
label:
'20, Wednesday, January 20, 2016'
,
label:
'20, Wednesday, January 20, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'21'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'21'
)),
matchesSemantics
(
label:
'21, Thursday, January 21, 2016'
,
label:
'21, Thursday, January 21, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'22'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'22'
)),
matchesSemantics
(
label:
'22, Friday, January 22, 2016'
,
label:
'22, Friday, January 22, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'23'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'23'
)),
matchesSemantics
(
label:
'23, Saturday, January 23, 2016'
,
label:
'23, Saturday, January 23, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'24'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'24'
)),
matchesSemantics
(
label:
'24, Sunday, January 24, 2016'
,
label:
'24, Sunday, January 24, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'25'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'25'
)),
matchesSemantics
(
label:
'25, Monday, January 25, 2016'
,
label:
'25, Monday, January 25, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'26'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'26'
)),
matchesSemantics
(
label:
'26, Tuesday, January 26, 2016'
,
label:
'26, Tuesday, January 26, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'27'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'27'
)),
matchesSemantics
(
label:
'27, Wednesday, January 27, 2016'
,
label:
'27, Wednesday, January 27, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'28'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'28'
)),
matchesSemantics
(
label:
'28, Thursday, January 28, 2016'
,
label:
'28, Thursday, January 28, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'29'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'29'
)),
matchesSemantics
(
label:
'29, Friday, January 29, 2016'
,
label:
'29, Friday, January 29, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
expect
(
tester
.
getSemantics
(
find
.
text
(
'30'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
text
(
'30'
)),
matchesSemantics
(
label:
'30, Saturday, January 30, 2016'
,
label:
'30, Saturday, January 30, 2016'
,
hasTapAction:
true
,
hasTapAction:
true
,
isFocusable:
true
,
));
));
// Ok/Cancel buttons
// Ok/Cancel buttons
...
@@ -1113,6 +1153,7 @@ void main() {
...
@@ -1113,6 +1153,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
// Should be in the input mode
// Should be in the input mode
...
@@ -1159,6 +1200,128 @@ void main() {
...
@@ -1159,6 +1200,128 @@ void main() {
expect
(
find
.
text
(
'March 2016'
),
findsOneWidget
);
expect
(
find
.
text
(
'March 2016'
),
findsOneWidget
);
});
});
});
});
testWidgets
(
'Can navigate date grid with arrow keys'
,
(
WidgetTester
tester
)
async
{
await
prepareDatePicker
(
tester
,
(
Future
<
DateTime
>
date
)
async
{
// Navigate to the grid
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
// Navigate from Jan 15 to Jan 18 with arrow keys
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
pumpAndSettle
();
// Activate it
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Navigate out of the grid and to the OK button
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
// Activate OK
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Should have selected Jan 18
expect
(
await
date
,
DateTime
(
2016
,
DateTime
.
january
,
18
));
});
});
testWidgets
(
'Navigating with arrow keys scrolls months'
,
(
WidgetTester
tester
)
async
{
await
prepareDatePicker
(
tester
,
(
Future
<
DateTime
>
date
)
async
{
// Navigate to the grid
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
pumpAndSettle
();
// Navigate from Jan 15 to Dec 31 with arrow keys
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
pumpAndSettle
();
// Should have scrolled to Dec 2015
expect
(
find
.
text
(
'December 2015'
),
findsOneWidget
);
// Navigate from Dec 31 to Nov 26 with arrow keys
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
pumpAndSettle
();
// Should have scrolled to Nov 2015
expect
(
find
.
text
(
'November 2015'
),
findsOneWidget
);
// Activate it
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Navigate out of the grid and to the OK button
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
pumpAndSettle
();
// Activate OK
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Should have selected Jan 18
expect
(
await
date
,
DateTime
(
2015
,
DateTime
.
november
,
26
));
});
});
testWidgets
(
'RTL text direction reverses the horizontal arrow key navigation'
,
(
WidgetTester
tester
)
async
{
await
prepareDatePicker
(
tester
,
(
Future
<
DateTime
>
date
)
async
{
// Navigate to the grid
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
pumpAndSettle
();
// Navigate from Jan 15 to 19 with arrow keys
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
pumpAndSettle
();
// Activate it
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Navigate out of the grid and to the OK button
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
tab
);
await
tester
.
pumpAndSettle
();
// Activate OK
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
space
);
await
tester
.
pumpAndSettle
();
// Should have selected Jan 18
expect
(
await
date
,
DateTime
(
2016
,
DateTime
.
january
,
19
));
},
textDirection:
TextDirection
.
rtl
);
});
});
});
group
(
'Screen configurations'
,
()
{
group
(
'Screen configurations'
,
()
{
...
...
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