Skip to content

Commit 913e21e

Browse files
authored
First pass at keyboard navigation for the Material Date Picker (flutter#59279)
1 parent 0093c6a commit 913e21e

File tree

2 files changed

+112
-61
lines changed

2 files changed

+112
-61
lines changed

packages/flutter/lib/src/material/pickers/date_picker_header.dart

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:flutter/widgets.dart';
88

99
import '../color_scheme.dart';
1010
import '../icon_button.dart';
11+
import '../material.dart';
1112
import '../text_theme.dart';
1213
import '../theme.dart';
1314

@@ -127,69 +128,65 @@ class DatePickerHeader extends StatelessWidget {
127128

128129
switch (orientation) {
129130
case Orientation.portrait:
130-
return Column(
131-
crossAxisAlignment: CrossAxisAlignment.start,
132-
children: <Widget>[
133-
Container(
134-
height: _datePickerHeaderPortraitHeight,
135-
color: primarySurfaceColor,
136-
padding: const EdgeInsetsDirectional.only(
137-
start: 24,
138-
end: 12,
139-
),
140-
child: Column(
141-
crossAxisAlignment: CrossAxisAlignment.start,
142-
children: <Widget>[
143-
const SizedBox(height: 16),
144-
Flexible(child: help),
145-
const SizedBox(height: 38),
146-
Row(
147-
children: <Widget>[
148-
Expanded(child: title),
149-
icon,
150-
],
151-
),
152-
],
153-
),
154-
),
155-
],
156-
);
131+
return SizedBox(
132+
height: _datePickerHeaderPortraitHeight,
133+
child: Material(
134+
color: primarySurfaceColor,
135+
child: Padding(
136+
padding: const EdgeInsetsDirectional.only(
137+
start: 24,
138+
end: 12,
139+
),
140+
child: Column(
141+
crossAxisAlignment: CrossAxisAlignment.start,
142+
children: <Widget>[
143+
const SizedBox(height: 16),
144+
Flexible(child: help),
145+
const SizedBox(height: 38),
146+
Row(
147+
children: <Widget>[
148+
Expanded(child: title),
149+
icon,
150+
],
151+
),
152+
],
153+
),
154+
),
155+
),
156+
);
157157
case Orientation.landscape:
158-
return Row(
159-
crossAxisAlignment: CrossAxisAlignment.start,
160-
children: <Widget>[
161-
Container(
162-
width: _datePickerHeaderLandscapeWidth,
163-
color: primarySurfaceColor,
164-
child: Column(
165-
crossAxisAlignment: CrossAxisAlignment.start,
166-
children: <Widget>[
167-
const SizedBox(height: 16),
168-
Padding(
158+
return SizedBox(
159+
width: _datePickerHeaderLandscapeWidth,
160+
child: Material(
161+
color: primarySurfaceColor,
162+
child: Column(
163+
crossAxisAlignment: CrossAxisAlignment.start,
164+
children: <Widget>[
165+
const SizedBox(height: 16),
166+
Padding(
167+
padding: const EdgeInsets.symmetric(
168+
horizontal: _headerPaddingLandscape,
169+
),
170+
child: help,
171+
),
172+
SizedBox(height: isShort ? 16 : 56),
173+
Expanded(
174+
child: Padding(
169175
padding: const EdgeInsets.symmetric(
170176
horizontal: _headerPaddingLandscape,
171177
),
172-
child: help,
178+
child: title,
173179
),
174-
SizedBox(height: isShort ? 16 : 56),
175-
Expanded(
176-
child: Padding(
177-
padding: const EdgeInsets.symmetric(
178-
horizontal: _headerPaddingLandscape,
179-
),
180-
child: title,
181-
),
182-
),
183-
Padding(
184-
padding: const EdgeInsets.symmetric(
185-
horizontal: 4,
186-
),
187-
child: icon,
180+
),
181+
Padding(
182+
padding: const EdgeInsets.symmetric(
183+
horizontal: 4,
188184
),
189-
],
190-
),
185+
child: icon,
186+
),
187+
],
191188
),
192-
],
189+
),
193190
);
194191
}
195192
return null;

packages/flutter/test/material/date_picker_test.dart

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -803,8 +803,7 @@ void main() {
803803

804804
await prepareDatePicker(tester, (Future<DateTime> date) async {
805805
// Header
806-
expect(
807-
tester.getSemantics(find.text('SELECT DATE')), matchesSemantics(
806+
expect(tester.getSemantics(find.text('SELECT DATE')), matchesSemantics(
808807
label: 'SELECT DATE\nFri, Jan 15',
809808
));
810809

@@ -819,8 +818,7 @@ void main() {
819818
));
820819

821820
// Year mode drop down button
822-
expect(
823-
tester.getSemantics(find.text('January 2016')), matchesSemantics(
821+
expect(tester.getSemantics(find.text('January 2016')), matchesSemantics(
824822
label: 'Select year',
825823
isButton: true,
826824
));
@@ -983,7 +981,6 @@ void main() {
983981
hasEnabledState: true,
984982
isFocusable: true,
985983
));
986-
987984
});
988985

989986
semantics.dispose();
@@ -1107,6 +1104,63 @@ void main() {
11071104
});
11081105
});
11091106

1107+
group('Keyboard navigation', () {
1108+
testWidgets('Can toggle to calendar entry mode', (WidgetTester tester) async {
1109+
await prepareDatePicker(tester, (Future<DateTime> date) async {
1110+
expect(find.byType(TextField), findsNothing);
1111+
// Navigate to the entry toggle button and activate it
1112+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1113+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1114+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1115+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1116+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1117+
await tester.pumpAndSettle();
1118+
// Should be in the input mode
1119+
expect(find.byType(TextField), findsOneWidget);
1120+
});
1121+
});
1122+
1123+
testWidgets('Can toggle to year mode', (WidgetTester tester) async {
1124+
await prepareDatePicker(tester, (Future<DateTime> date) async {
1125+
expect(find.text('2016'), findsNothing);
1126+
// Navigate to the year selector and activate it
1127+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1128+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1129+
await tester.pumpAndSettle();
1130+
// The years should be visible
1131+
expect(find.text('2016'), findsOneWidget);
1132+
});
1133+
});
1134+
1135+
testWidgets('Can navigate next/previous months', (WidgetTester tester) async {
1136+
await prepareDatePicker(tester, (Future<DateTime> date) async {
1137+
expect(find.text('January 2016'), findsOneWidget);
1138+
// Navigate to the previous month button and activate it twice
1139+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1140+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1141+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1142+
await tester.pumpAndSettle();
1143+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1144+
await tester.pumpAndSettle();
1145+
// Should be showing Nov 2015
1146+
expect(find.text('November 2015'), findsOneWidget);
1147+
1148+
// Navigate to the next month button and activate it four times
1149+
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1150+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1151+
await tester.pumpAndSettle();
1152+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1153+
await tester.pumpAndSettle();
1154+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1155+
await tester.pumpAndSettle();
1156+
await tester.sendKeyEvent(LogicalKeyboardKey.space);
1157+
await tester.pumpAndSettle();
1158+
// Should be on Mar 2016
1159+
expect(find.text('March 2016'), findsOneWidget);
1160+
});
1161+
});
1162+
});
1163+
11101164
group('Screen configurations', () {
11111165
// Test various combinations of screen sizes, orientations and text scales
11121166
// to ensure the layout doesn't overflow and cause an exception to be thrown.

0 commit comments

Comments
 (0)