Skip to content

Commit 14b914a

Browse files
authored
Reland [a11y] Fix date picker cannot focus on the edit field (#144198)
reland flutter/flutter#143117 fixes: flutter/flutter#143116 fixes: flutter/flutter#141992 ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [Data Driven Fixes]: https://github.com/flutter/flutter/wiki/Data-driven-Fixes
1 parent e41ffcb commit 14b914a

File tree

4 files changed

+102
-63
lines changed

4 files changed

+102
-63
lines changed

packages/flutter/lib/src/material/calendar_date_picker.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ class _DatePickerModeToggleButtonState extends State<_DatePickerModeToggleButton
409409
label: MaterialLocalizations.of(context).selectYearSemanticsLabel,
410410
excludeSemantics: true,
411411
button: true,
412+
container: true,
412413
child: SizedBox(
413414
height: _subHeaderHeight,
414415
child: InkWell(

packages/flutter/lib/src/material/date_picker.dart

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -861,64 +861,76 @@ class _DatePickerHeader extends StatelessWidget {
861861

862862
switch (orientation) {
863863
case Orientation.portrait:
864-
return SizedBox(
865-
height: _datePickerHeaderPortraitHeight,
866-
child: Material(
867-
color: backgroundColor,
868-
child: Padding(
869-
padding: const EdgeInsetsDirectional.only(
870-
start: 24,
871-
end: 12,
872-
bottom: 12,
873-
),
874-
child: Column(
875-
crossAxisAlignment: CrossAxisAlignment.start,
876-
children: <Widget>[
877-
const SizedBox(height: 16),
878-
help,
879-
const Flexible(child: SizedBox(height: 38)),
880-
Row(
881-
children: <Widget>[
882-
Expanded(child: title),
883-
if (entryModeButton != null)
884-
entryModeButton!,
885-
],
886-
),
887-
],
864+
return Semantics(
865+
container: true,
866+
child: SizedBox(
867+
height: _datePickerHeaderPortraitHeight,
868+
child: Material(
869+
color: backgroundColor,
870+
child: Padding(
871+
padding: const EdgeInsetsDirectional.only(
872+
start: 24,
873+
end: 12,
874+
bottom: 12,
875+
),
876+
child: Column(
877+
crossAxisAlignment: CrossAxisAlignment.start,
878+
children: <Widget>[
879+
const SizedBox(height: 16),
880+
help,
881+
const Flexible(child: SizedBox(height: 38)),
882+
Row(
883+
children: <Widget>[
884+
Expanded(child: title),
885+
if (entryModeButton != null)
886+
Semantics(
887+
container: true,
888+
child: entryModeButton,
889+
),
890+
],
891+
),
892+
],
893+
),
888894
),
889895
),
890896
),
891897
);
892898
case Orientation.landscape:
893-
return SizedBox(
894-
width: _datePickerHeaderLandscapeWidth,
895-
child: Material(
896-
color: backgroundColor,
897-
child: Column(
898-
crossAxisAlignment: CrossAxisAlignment.start,
899-
children: <Widget>[
900-
const SizedBox(height: 16),
901-
Padding(
902-
padding: const EdgeInsets.symmetric(
903-
horizontal: _headerPaddingLandscape,
904-
),
905-
child: help,
906-
),
907-
SizedBox(height: isShort ? 16 : 56),
908-
Expanded(
909-
child: Padding(
899+
return Semantics(
900+
container: true,
901+
child:SizedBox(
902+
width: _datePickerHeaderLandscapeWidth,
903+
child: Material(
904+
color: backgroundColor,
905+
child: Column(
906+
crossAxisAlignment: CrossAxisAlignment.start,
907+
children: <Widget>[
908+
const SizedBox(height: 16),
909+
Padding(
910910
padding: const EdgeInsets.symmetric(
911911
horizontal: _headerPaddingLandscape,
912912
),
913-
child: title,
913+
child: help,
914914
),
915-
),
916-
if (entryModeButton != null)
917-
Padding(
918-
padding: const EdgeInsets.symmetric(horizontal: 4),
919-
child: entryModeButton,
915+
SizedBox(height: isShort ? 16 : 56),
916+
Expanded(
917+
child: Padding(
918+
padding: const EdgeInsets.symmetric(
919+
horizontal: _headerPaddingLandscape,
920+
),
921+
child: title,
922+
),
920923
),
921-
],
924+
if (entryModeButton != null)
925+
Padding(
926+
padding: const EdgeInsets.symmetric(horizontal: 4),
927+
child: Semantics(
928+
container: true,
929+
child: entryModeButton,
930+
),
931+
),
932+
],
933+
),
922934
),
923935
),
924936
);

packages/flutter/lib/src/material/input_date_picker_form_field.dart

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -256,21 +256,24 @@ class _InputDatePickerFormFieldState extends State<InputDatePickerFormField> {
256256
?? theme.inputDecorationTheme.border
257257
?? (useMaterial3 ? const OutlineInputBorder() : const UnderlineInputBorder());
258258

259-
return TextFormField(
260-
decoration: InputDecoration(
261-
hintText: widget.fieldHintText ?? localizations.dateHelpText,
262-
labelText: widget.fieldLabelText ?? localizations.dateInputLabel,
263-
).applyDefaults(inputTheme
264-
.merge(datePickerTheme.inputDecorationTheme)
265-
.copyWith(border: effectiveInputBorder),
259+
return Semantics(
260+
container: true,
261+
child: TextFormField(
262+
decoration: InputDecoration(
263+
hintText: widget.fieldHintText ?? localizations.dateHelpText,
264+
labelText: widget.fieldLabelText ?? localizations.dateInputLabel,
265+
).applyDefaults(inputTheme
266+
.merge(datePickerTheme.inputDecorationTheme)
267+
.copyWith(border: effectiveInputBorder),
268+
),
269+
validator: _validateDate,
270+
keyboardType: widget.keyboardType ?? TextInputType.datetime,
271+
onSaved: _handleSaved,
272+
onFieldSubmitted: _handleSubmitted,
273+
autofocus: widget.autofocus,
274+
controller: _controller,
275+
focusNode: widget.focusNode,
266276
),
267-
validator: _validateDate,
268-
keyboardType: widget.keyboardType ?? TextInputType.datetime,
269-
onSaved: _handleSaved,
270-
onFieldSubmitted: _handleSubmitted,
271-
autofocus: widget.autofocus,
272-
controller: _controller,
273-
focusNode: widget.focusNode,
274277
);
275278
}
276279
}

packages/flutter/test/material/date_picker_test.dart

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:flutter/foundation.dart';
1313
import 'package:flutter/material.dart';
1414
import 'package:flutter/services.dart';
1515
import 'package:flutter_test/flutter_test.dart';
16+
import '../widgets/clipboard_utils.dart';
1617

1718
void main() {
1819
TestWidgetsFlutterBinding.ensureInitialized();
@@ -1577,6 +1578,13 @@ void main() {
15771578
});
15781579

15791580
testWidgets('input mode', (WidgetTester tester) async {
1581+
// Fill the clipboard so that the Paste option is available in the text
1582+
// selection menu.
1583+
final MockClipboard mockClipboard = MockClipboard();
1584+
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, mockClipboard.handleMethodCall);
1585+
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
1586+
addTearDown(() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null));
1587+
15801588
final SemanticsHandle semantics = tester.ensureSemantics();
15811589

15821590
initialEntryMode = DatePickerEntryMode.input;
@@ -1596,7 +1604,22 @@ void main() {
15961604
isFocusable: true,
15971605
));
15981606

1599-
// The semantics of the InputDatePickerFormField are tested in its tests.
1607+
expect(tester.getSemantics(find.byType(EditableText)), matchesSemantics(
1608+
label: 'Enter Date',
1609+
isEnabled: true,
1610+
hasEnabledState: true,
1611+
isTextField: true,
1612+
isFocused: true,
1613+
value: '01/15/2016',
1614+
hasTapAction: true,
1615+
hasSetTextAction: true,
1616+
hasSetSelectionAction: true,
1617+
hasCopyAction: true,
1618+
hasCutAction: true,
1619+
hasPasteAction: true,
1620+
hasMoveCursorBackwardByCharacterAction: true,
1621+
hasMoveCursorBackwardByWordAction: true,
1622+
));
16001623

16011624
// Ok/Cancel buttons
16021625
expect(tester.getSemantics(find.text('OK')), matchesSemantics(

0 commit comments

Comments
 (0)