From ff1fd2aee37d397bc24e9fbcf8cf20f58ea41c11 Mon Sep 17 00:00:00 2001 From: Bruno Leroux Date: Sat, 20 Sep 2025 08:30:31 +0200 Subject: [PATCH] [a11y] TimePicker clock is unnecessarily announced (#175570) ## Description This PR removes the Semantics for the dial in TimePicker. This Semantics was meant to announce the input mode (hour or minute) but this is already done when tapping in the hour or minute fields. ## Related Issue Fixes [[Google3 Bug]: TimePicker clock is unnecessarily announced to TalkBack users](https://github.com/flutter/flutter/issues/175369) ## Tests Updates 1 test. --- .../flutter/lib/src/material/time_picker.dart | 29 ++++++--------- .../test/material/time_picker_test.dart | 37 +++++-------------- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index 6d60b2bcfc5..9cc64748e60 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -3039,24 +3039,17 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin { final Widget dial = Padding( padding: dialPadding, - child: Semantics( - label: switch (_hourMinuteMode.value) { - _HourMinuteMode.hour => localizations.timePickerHourModeAnnouncement, - _HourMinuteMode.minute => localizations.timePickerMinuteModeAnnouncement, - }, - liveRegion: true, - child: ExcludeSemantics( - child: SizedBox.fromSize( - size: defaultTheme.dialSize, - child: AspectRatio( - aspectRatio: 1, - child: _Dial( - hourMinuteMode: _hourMinuteMode.value, - hourDialType: hourMode, - selectedTime: _selectedTime.value, - onChanged: _handleTimeChanged, - onHourSelected: _handleHourSelected, - ), + child: ExcludeSemantics( + child: SizedBox.fromSize( + size: defaultTheme.dialSize, + child: AspectRatio( + aspectRatio: 1, + child: _Dial( + hourMinuteMode: _hourMinuteMode.value, + hourDialType: hourMode, + selectedTime: _selectedTime.value, + onChanged: _handleTimeChanged, + onHourSelected: _handleHourSelected, ), ), ), diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 81802d553d6..2dcc556be8a 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -2424,41 +2424,24 @@ void main() { WidgetTester tester, ) async { final SemanticsTester semantics = SemanticsTester(tester); - await mediaQueryBoilerplate(tester, materialType: MaterialType.material3); + const TimeOfDay time = TimeOfDay(hour: 8, minute: 12); + + await mediaQueryBoilerplate(tester, initialTime: time, materialType: MaterialType.material3); final MaterialLocalizations localizations = MaterialLocalizations.of( tester.element(find.byType(TimePickerDialog)), ); - final Finder semanticsFinder = find.bySemanticsLabel( - localizations.timePickerHourModeAnnouncement, - ); - final SemanticsNode semanticsNode = tester.getSemantics(semanticsFinder); + final String formattedHour = localizations.formatHour(time); + final String formattedMinute = localizations.formatMinute(time); + expect( - semanticsNode.label, - localizations.timePickerHourModeAnnouncement, - reason: 'Label should announce hour mode initially', + find.semantics.byValue('${localizations.timePickerHourModeAnnouncement} $formattedHour'), + findsOne, ); expect( - semanticsNode.hasFlag(SemanticsFlag.isLiveRegion), - isTrue, - reason: 'Node should be a live region to announce changes', - ); - - // --- Switch to minute mode --- - final Finder minuteControlInkWell = find.descendant( - of: _minuteControl, - matching: find.byType(InkWell), - ); - expect(minuteControlInkWell, findsOneWidget, reason: 'Minute control should exist'); - await tester.tap(minuteControlInkWell); - await tester.pumpAndSettle(); - - // Get the updated node properties - expect( - semanticsNode.label, - localizations.timePickerMinuteModeAnnouncement, - reason: 'Label should announce minute mode after switching', + find.semantics.byValue('${localizations.timePickerMinuteModeAnnouncement} $formattedMinute'), + findsOne, ); semantics.dispose();