mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Reland [a11y] Fix date picker cannot focus on the edit field (#144198)
reland https://github.com/flutter/flutter/pull/143117 fixes: https://github.com/flutter/flutter/issues/143116 fixes: https://github.com/flutter/flutter/issues/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
This commit is contained in:
parent
e41ffcb742
commit
14b914ab92
@ -409,6 +409,7 @@ class _DatePickerModeToggleButtonState extends State<_DatePickerModeToggleButton
|
|||||||
label: MaterialLocalizations.of(context).selectYearSemanticsLabel,
|
label: MaterialLocalizations.of(context).selectYearSemanticsLabel,
|
||||||
excludeSemantics: true,
|
excludeSemantics: true,
|
||||||
button: true,
|
button: true,
|
||||||
|
container: true,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: _subHeaderHeight,
|
height: _subHeaderHeight,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
@ -861,64 +861,76 @@ class _DatePickerHeader extends StatelessWidget {
|
|||||||
|
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
case Orientation.portrait:
|
case Orientation.portrait:
|
||||||
return SizedBox(
|
return Semantics(
|
||||||
height: _datePickerHeaderPortraitHeight,
|
container: true,
|
||||||
child: Material(
|
child: SizedBox(
|
||||||
color: backgroundColor,
|
height: _datePickerHeaderPortraitHeight,
|
||||||
child: Padding(
|
child: Material(
|
||||||
padding: const EdgeInsetsDirectional.only(
|
color: backgroundColor,
|
||||||
start: 24,
|
child: Padding(
|
||||||
end: 12,
|
padding: const EdgeInsetsDirectional.only(
|
||||||
bottom: 12,
|
start: 24,
|
||||||
),
|
end: 12,
|
||||||
child: Column(
|
bottom: 12,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: <Widget>[
|
child: Column(
|
||||||
const SizedBox(height: 16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
help,
|
children: <Widget>[
|
||||||
const Flexible(child: SizedBox(height: 38)),
|
const SizedBox(height: 16),
|
||||||
Row(
|
help,
|
||||||
children: <Widget>[
|
const Flexible(child: SizedBox(height: 38)),
|
||||||
Expanded(child: title),
|
Row(
|
||||||
if (entryModeButton != null)
|
children: <Widget>[
|
||||||
entryModeButton!,
|
Expanded(child: title),
|
||||||
],
|
if (entryModeButton != null)
|
||||||
),
|
Semantics(
|
||||||
],
|
container: true,
|
||||||
|
child: entryModeButton,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case Orientation.landscape:
|
case Orientation.landscape:
|
||||||
return SizedBox(
|
return Semantics(
|
||||||
width: _datePickerHeaderLandscapeWidth,
|
container: true,
|
||||||
child: Material(
|
child:SizedBox(
|
||||||
color: backgroundColor,
|
width: _datePickerHeaderLandscapeWidth,
|
||||||
child: Column(
|
child: Material(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
color: backgroundColor,
|
||||||
children: <Widget>[
|
child: Column(
|
||||||
const SizedBox(height: 16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Padding(
|
children: <Widget>[
|
||||||
padding: const EdgeInsets.symmetric(
|
const SizedBox(height: 16),
|
||||||
horizontal: _headerPaddingLandscape,
|
Padding(
|
||||||
),
|
|
||||||
child: help,
|
|
||||||
),
|
|
||||||
SizedBox(height: isShort ? 16 : 56),
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: _headerPaddingLandscape,
|
horizontal: _headerPaddingLandscape,
|
||||||
),
|
),
|
||||||
child: title,
|
child: help,
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: isShort ? 16 : 56),
|
||||||
if (entryModeButton != null)
|
Expanded(
|
||||||
Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(
|
||||||
child: entryModeButton,
|
horizontal: _headerPaddingLandscape,
|
||||||
|
),
|
||||||
|
child: title,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
if (entryModeButton != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: Semantics(
|
||||||
|
container: true,
|
||||||
|
child: entryModeButton,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -256,21 +256,24 @@ class _InputDatePickerFormFieldState extends State<InputDatePickerFormField> {
|
|||||||
?? theme.inputDecorationTheme.border
|
?? theme.inputDecorationTheme.border
|
||||||
?? (useMaterial3 ? const OutlineInputBorder() : const UnderlineInputBorder());
|
?? (useMaterial3 ? const OutlineInputBorder() : const UnderlineInputBorder());
|
||||||
|
|
||||||
return TextFormField(
|
return Semantics(
|
||||||
decoration: InputDecoration(
|
container: true,
|
||||||
hintText: widget.fieldHintText ?? localizations.dateHelpText,
|
child: TextFormField(
|
||||||
labelText: widget.fieldLabelText ?? localizations.dateInputLabel,
|
decoration: InputDecoration(
|
||||||
).applyDefaults(inputTheme
|
hintText: widget.fieldHintText ?? localizations.dateHelpText,
|
||||||
.merge(datePickerTheme.inputDecorationTheme)
|
labelText: widget.fieldLabelText ?? localizations.dateInputLabel,
|
||||||
.copyWith(border: effectiveInputBorder),
|
).applyDefaults(inputTheme
|
||||||
|
.merge(datePickerTheme.inputDecorationTheme)
|
||||||
|
.copyWith(border: effectiveInputBorder),
|
||||||
|
),
|
||||||
|
validator: _validateDate,
|
||||||
|
keyboardType: widget.keyboardType ?? TextInputType.datetime,
|
||||||
|
onSaved: _handleSaved,
|
||||||
|
onFieldSubmitted: _handleSubmitted,
|
||||||
|
autofocus: widget.autofocus,
|
||||||
|
controller: _controller,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
),
|
),
|
||||||
validator: _validateDate,
|
|
||||||
keyboardType: widget.keyboardType ?? TextInputType.datetime,
|
|
||||||
onSaved: _handleSaved,
|
|
||||||
onFieldSubmitted: _handleSubmitted,
|
|
||||||
autofocus: widget.autofocus,
|
|
||||||
controller: _controller,
|
|
||||||
focusNode: widget.focusNode,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import '../widgets/clipboard_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -1577,6 +1578,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('input mode', (WidgetTester tester) async {
|
testWidgets('input mode', (WidgetTester tester) async {
|
||||||
|
// Fill the clipboard so that the Paste option is available in the text
|
||||||
|
// selection menu.
|
||||||
|
final MockClipboard mockClipboard = MockClipboard();
|
||||||
|
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, mockClipboard.handleMethodCall);
|
||||||
|
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
|
||||||
|
addTearDown(() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null));
|
||||||
|
|
||||||
final SemanticsHandle semantics = tester.ensureSemantics();
|
final SemanticsHandle semantics = tester.ensureSemantics();
|
||||||
|
|
||||||
initialEntryMode = DatePickerEntryMode.input;
|
initialEntryMode = DatePickerEntryMode.input;
|
||||||
@ -1596,7 +1604,22 @@ void main() {
|
|||||||
isFocusable: true,
|
isFocusable: true,
|
||||||
));
|
));
|
||||||
|
|
||||||
// The semantics of the InputDatePickerFormField are tested in its tests.
|
expect(tester.getSemantics(find.byType(EditableText)), matchesSemantics(
|
||||||
|
label: 'Enter Date',
|
||||||
|
isEnabled: true,
|
||||||
|
hasEnabledState: true,
|
||||||
|
isTextField: true,
|
||||||
|
isFocused: true,
|
||||||
|
value: '01/15/2016',
|
||||||
|
hasTapAction: true,
|
||||||
|
hasSetTextAction: true,
|
||||||
|
hasSetSelectionAction: true,
|
||||||
|
hasCopyAction: true,
|
||||||
|
hasCutAction: true,
|
||||||
|
hasPasteAction: true,
|
||||||
|
hasMoveCursorBackwardByCharacterAction: true,
|
||||||
|
hasMoveCursorBackwardByWordAction: true,
|
||||||
|
));
|
||||||
|
|
||||||
// Ok/Cancel buttons
|
// Ok/Cancel buttons
|
||||||
expect(tester.getSemantics(find.text('OK')), matchesSemantics(
|
expect(tester.getSemantics(find.text('OK')), matchesSemantics(
|
||||||
|
Loading…
Reference in New Issue
Block a user