mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Highlighting hour and minute input fields (#167766)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> This PR addresses the issue in time_picker.dart where pressing the TAB key to navigate between the hour and minute fields in input mode doesn't properly select the text in the newly focused field. **What Changed** - Added focus traversal logic in the _HourMinuteTextFieldState class's initState method that: 1. Properly handles TAB key presses to move focus to the next field 2. Automatically selects all text when a field gains focus - Added tests in time_picker_test.dart to verify that: 1. The hour field's text is selected when it gains focus 2. Pressing TAB moves focus to the minute field 3. The minute field's text is selected when it receives focus via TAB key **Why This Matters** This change improves accessibility and usability of the time picker in input mode. When users navigate using the keyboard (pressing TAB), they expect the text in the newly focused field to be fully selected, allowing them to immediately type a new value without having to manually delete the existing text. This is standard behavior in form fields and now the TimePicker component follows this expected pattern. **Before the change:** web: https://time-picker-0423-before-change.web.app/ mobile: https://github.com/user-attachments/assets/532c8aa2-4f8d-4118-9eaa-2fc6c2825486 **After the change:** web: https://time-picker-04232025.web.app/ mobile: https://github.com/user-attachments/assets/9271b3b0-9ece-479e-a01e-a62e31b1d6c7 **Issue to fix:** https://github.com/flutter/flutter/issues/165830 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] 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/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#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/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
41a5cbdf70
commit
afb08bba88
@ -2035,6 +2035,12 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora
|
||||
FocusNode()..addListener(() {
|
||||
setState(() {
|
||||
// Rebuild when focus changes.
|
||||
if (kIsWeb && focusNode.hasFocus && primaryFocus?.context != null) {
|
||||
Actions.maybeInvoke(
|
||||
primaryFocus!.context!,
|
||||
const SelectAllTextIntent(SelectionChangedCause.keyboard),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -8,8 +8,10 @@ library;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@ -2075,6 +2077,45 @@ void main() {
|
||||
expect(hourField.focusNode!.hasFocus, isFalse);
|
||||
expect(minuteField.focusNode!.hasFocus, isFalse);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'TAB key selects text in hour and minute fields on the web',
|
||||
(WidgetTester tester) async {
|
||||
await mediaQueryBoilerplate(
|
||||
tester,
|
||||
entryMode: TimePickerEntryMode.input,
|
||||
materialType: materialType,
|
||||
);
|
||||
|
||||
// Focus on the hour field.
|
||||
final Finder hourField = find.byType(TextField).first;
|
||||
await tester.tap(hourField);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify that the hour field is focused and its text is selected.
|
||||
final TextField hourTextField = tester.widget(hourField);
|
||||
expect(hourTextField.focusNode!.hasFocus, isTrue);
|
||||
expect(hourTextField.controller!.selection.baseOffset, 0);
|
||||
expect(
|
||||
hourTextField.controller!.selection.extentOffset,
|
||||
hourTextField.controller!.text.length,
|
||||
);
|
||||
|
||||
// Press TAB to move to the minute field.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify that the minute field is focused and its text is selected.
|
||||
final Finder minuteField = find.byType(TextField).last;
|
||||
final TextField minuteTextField = tester.widget(minuteField);
|
||||
expect(minuteTextField.controller!.selection.baseOffset, 0);
|
||||
expect(
|
||||
minuteTextField.controller!.selection.extentOffset,
|
||||
minuteTextField.controller!.text.length,
|
||||
);
|
||||
},
|
||||
skip: !kIsWeb, // [intended] Web-specific behavior
|
||||
);
|
||||
});
|
||||
|
||||
group('Time picker - Restoration (${materialType.name})', () {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user