@@ -250,81 +250,89 @@ class _TimePickerHeader extends StatelessWidget {
250250 ).timeOfDayFormat (alwaysUse24HourFormat: _TimePickerModel .use24HourFormatOf (context));
251251
252252 final _HourDialType hourDialType = _TimePickerModel .hourDialTypeOf (context);
253- switch (_TimePickerModel .orientationOf (context)) {
254- case Orientation .portrait:
255- return Column (
256- crossAxisAlignment: CrossAxisAlignment .start,
257- children: < Widget > [
258- Padding (
259- padding: EdgeInsetsDirectional .only (
260- bottom: _TimePickerModel .useMaterial3Of (context) ? 20 : 24 ,
261- ),
262- child: Text (
263- helpText,
264- style:
265- _TimePickerModel .themeOf (context).helpTextStyle ??
266- _TimePickerModel .defaultThemeOf (context).helpTextStyle,
253+ final RenderObjectWidget orientationSpecificHeader = switch (_TimePickerModel .orientationOf (
254+ context,
255+ )) {
256+ Orientation .portrait => Column (
257+ crossAxisAlignment: CrossAxisAlignment .start,
258+ children: < Widget > [
259+ Padding (
260+ padding: EdgeInsetsDirectional .only (
261+ bottom: _TimePickerModel .useMaterial3Of (context) ? 20 : 24 ,
262+ ),
263+ child: Text (
264+ helpText,
265+ style:
266+ _TimePickerModel .themeOf (context).helpTextStyle ??
267+ _TimePickerModel .defaultThemeOf (context).helpTextStyle,
268+ ),
269+ ),
270+ Row (
271+ textDirection:
272+ timeOfDayFormat == TimeOfDayFormat .a_space_h_colon_mm
273+ ? TextDirection .rtl
274+ : TextDirection .ltr,
275+ spacing: 12 ,
276+ children: < Widget > [
277+ Expanded (
278+ child: Row (
279+ // Hour/minutes should not change positions in RTL locales.
280+ textDirection: TextDirection .ltr,
281+ children: < Widget > [
282+ const Expanded (child: _HourControl ()),
283+ _TimeSelectorSeparator (timeOfDayFormat: timeOfDayFormat),
284+ const Expanded (child: _MinuteControl ()),
285+ ],
286+ ),
267287 ),
288+ if (hourDialType == _HourDialType .twelveHour) const _DayPeriodControl (),
289+ ],
290+ ),
291+ ],
292+ ),
293+ Orientation .landscape => SizedBox (
294+ width: _kTimePickerHeaderLandscapeWidth,
295+ child: Stack (
296+ children: < Widget > [
297+ Text (
298+ helpText,
299+ style:
300+ _TimePickerModel .themeOf (context).helpTextStyle ??
301+ _TimePickerModel .defaultThemeOf (context).helpTextStyle,
268302 ),
269- Row (
270- textDirection :
303+ Column (
304+ verticalDirection :
271305 timeOfDayFormat == TimeOfDayFormat .a_space_h_colon_mm
272- ? TextDirection .rtl
273- : TextDirection .ltr,
306+ ? VerticalDirection .up
307+ : VerticalDirection .down,
308+ mainAxisAlignment: MainAxisAlignment .center,
309+ crossAxisAlignment: CrossAxisAlignment .start,
274310 spacing: 12 ,
275311 children: < Widget > [
276- Expanded (
277- child: Row (
278- // Hour/minutes should not change positions in RTL locales.
279- textDirection: TextDirection .ltr,
280- children: < Widget > [
281- const Expanded (child: _HourControl ()),
282- _TimeSelectorSeparator (timeOfDayFormat: timeOfDayFormat),
283- const Expanded (child: _MinuteControl ()),
284- ],
285- ),
312+ Row (
313+ // Hour/minutes should not change positions in RTL locales.
314+ textDirection: TextDirection .ltr,
315+ children: < Widget > [
316+ const Expanded (child: _HourControl ()),
317+ _TimeSelectorSeparator (timeOfDayFormat: timeOfDayFormat),
318+ const Expanded (child: _MinuteControl ()),
319+ ],
286320 ),
287321 if (hourDialType == _HourDialType .twelveHour) const _DayPeriodControl (),
288322 ],
289323 ),
290324 ],
291- );
292- case Orientation .landscape:
293- return SizedBox (
294- width: _kTimePickerHeaderLandscapeWidth,
295- child: Stack (
296- children: < Widget > [
297- Text (
298- helpText,
299- style:
300- _TimePickerModel .themeOf (context).helpTextStyle ??
301- _TimePickerModel .defaultThemeOf (context).helpTextStyle,
302- ),
303- Column (
304- verticalDirection:
305- timeOfDayFormat == TimeOfDayFormat .a_space_h_colon_mm
306- ? VerticalDirection .up
307- : VerticalDirection .down,
308- mainAxisAlignment: MainAxisAlignment .center,
309- crossAxisAlignment: CrossAxisAlignment .start,
310- spacing: 12 ,
311- children: < Widget > [
312- Row (
313- // Hour/minutes should not change positions in RTL locales.
314- textDirection: TextDirection .ltr,
315- children: < Widget > [
316- const Expanded (child: _HourControl ()),
317- _TimeSelectorSeparator (timeOfDayFormat: timeOfDayFormat),
318- const Expanded (child: _MinuteControl ()),
319- ],
320- ),
321- if (hourDialType == _HourDialType .twelveHour) const _DayPeriodControl (),
322- ],
323- ),
324- ],
325- ),
326- );
327- }
325+ ),
326+ ),
327+ };
328+
329+ return Semantics (
330+ label: MaterialLocalizations .of (context).formatTimeOfDay (
331+ _TimePickerModel .selectedTimeOf (context),
332+ alwaysUse24HourFormat: MediaQuery .alwaysUse24HourFormatOf (context),
333+ ),
334+ child: orientationSpecificHeader,
335+ );
328336 }
329337}
330338
@@ -443,11 +451,7 @@ class _HourControl extends StatelessWidget {
443451 child: _HourMinuteControl (
444452 isSelected: _TimePickerModel .hourMinuteModeOf (context) == _HourMinuteMode .hour,
445453 text: formattedHour,
446- onTap:
447- Feedback .wrapForTap (
448- () => _TimePickerModel .setHourMinuteMode (context, _HourMinuteMode .hour),
449- context,
450- )! ,
454+ onTap: () => _TimePickerModel .setHourMinuteMode (context, _HourMinuteMode .hour),
451455 onDoubleTap:
452456 _TimePickerModel .of (context, _TimePickerAspect .onHourDoubleTapped).onHourDoubleTapped,
453457 ),
@@ -559,11 +563,7 @@ class _MinuteControl extends StatelessWidget {
559563 child: _HourMinuteControl (
560564 isSelected: _TimePickerModel .hourMinuteModeOf (context) == _HourMinuteMode .minute,
561565 text: formattedMinute,
562- onTap:
563- Feedback .wrapForTap (
564- () => _TimePickerModel .setHourMinuteMode (context, _HourMinuteMode .minute),
565- context,
566- )! ,
566+ onTap: () => _TimePickerModel .setHourMinuteMode (context, _HourMinuteMode .minute),
567567 onDoubleTap:
568568 _TimePickerModel .of (
569569 context,
@@ -597,19 +597,6 @@ class _DayPeriodControl extends StatelessWidget {
597597 if (selectedTime.period == DayPeriod .am) {
598598 return ;
599599 }
600- switch (Theme .of (context).platform) {
601- case TargetPlatform .android:
602- case TargetPlatform .fuchsia:
603- case TargetPlatform .linux:
604- case TargetPlatform .windows:
605- _announceToAccessibility (
606- context,
607- MaterialLocalizations .of (context).anteMeridiemAbbreviation,
608- );
609- case TargetPlatform .iOS:
610- case TargetPlatform .macOS:
611- break ;
612- }
613600 _togglePeriod (context);
614601 }
615602
@@ -618,19 +605,6 @@ class _DayPeriodControl extends StatelessWidget {
618605 if (selectedTime.period == DayPeriod .pm) {
619606 return ;
620607 }
621- switch (Theme .of (context).platform) {
622- case TargetPlatform .android:
623- case TargetPlatform .fuchsia:
624- case TargetPlatform .linux:
625- case TargetPlatform .windows:
626- _announceToAccessibility (
627- context,
628- MaterialLocalizations .of (context).postMeridiemAbbreviation,
629- );
630- case TargetPlatform .iOS:
631- case TargetPlatform .macOS:
632- break ;
633- }
634608 _togglePeriod (context);
635609 }
636610
@@ -758,7 +732,7 @@ class _AmPmButton extends StatelessWidget {
758732 return Material (
759733 color: resolvedBackgroundColor,
760734 child: InkWell (
761- onTap: Feedback . wrapForTap ( onPressed, context) ,
735+ onTap: onPressed,
762736 child: Semantics (
763737 checked: selected,
764738 inMutuallyExclusiveGroup: true ,
@@ -1328,18 +1302,9 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
13281302 _center = box.size.center (Offset .zero);
13291303 _dialSize = box.size;
13301304 _updateThetaForPan (roundMinutes: true );
1331- final TimeOfDay newTime = _notifyOnChangedIfNeeded (roundMinutes: true );
1305+ _notifyOnChangedIfNeeded (roundMinutes: true );
13321306 if (widget.hourMinuteMode == _HourMinuteMode .hour) {
1333- switch (widget.hourDialType) {
1334- case _HourDialType .twentyFourHour:
1335- case _HourDialType .twentyFourHourDoubleRing:
1336- _announceToAccessibility (context, localizations.formatDecimal (newTime.hour));
1337- case _HourDialType .twelveHour:
1338- _announceToAccessibility (context, localizations.formatDecimal (newTime.hourOfPeriod));
1339- }
13401307 widget.onHourSelected? .call ();
1341- } else {
1342- _announceToAccessibility (context, localizations.formatDecimal (newTime.minute));
13431308 }
13441309 final TimeOfDay time = _getTimeForTheta (
13451310 _theta.value,
@@ -1354,7 +1319,6 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
13541319 }
13551320
13561321 void _selectHour (int hour) {
1357- _announceToAccessibility (context, localizations.formatDecimal (hour));
13581322 final TimeOfDay time;
13591323
13601324 TimeOfDay getAmPmTime () {
@@ -1387,7 +1351,6 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
13871351 }
13881352
13891353 void _selectMinute (int minute) {
1390- _announceToAccessibility (context, localizations.formatDecimal (minute));
13911354 final TimeOfDay time = TimeOfDay (hour: widget.selectedTime.hour, minute: minute);
13921355 final double angle = _getThetaForTime (time);
13931356 _thetaTween
@@ -2775,7 +2738,6 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
27752738 );
27762739 final RestorableBoolN _autofocusHour = RestorableBoolN (null );
27772740 final RestorableBoolN _autofocusMinute = RestorableBoolN (null );
2778- final RestorableBool _announcedInitialTime = RestorableBool (false );
27792741 late final RestorableEnumN <Orientation > _orientation = RestorableEnumN <Orientation >(
27802742 widget.orientation,
27812743 values: Orientation .values,
@@ -2793,16 +2755,13 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
27932755 _lastModeAnnounced.dispose ();
27942756 _autofocusHour.dispose ();
27952757 _autofocusMinute.dispose ();
2796- _announcedInitialTime.dispose ();
27972758 super .dispose ();
27982759 }
27992760
28002761 @override
28012762 void didChangeDependencies () {
28022763 super .didChangeDependencies ();
28032764 localizations = MaterialLocalizations .of (context);
2804- _announceInitialTimeOnce ();
2805- _announceModeOnce ();
28062765 }
28072766
28082767 @override
@@ -2829,7 +2788,6 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
28292788 registerForRestoration (_lastModeAnnounced, 'last_mode_announced' );
28302789 registerForRestoration (_autofocusHour, 'autofocus_hour' );
28312790 registerForRestoration (_autofocusMinute, 'autofocus_minute' );
2832- registerForRestoration (_announcedInitialTime, 'announced_initial_time' );
28332791 registerForRestoration (_selectedTime, 'selected_time' );
28342792 registerForRestoration (_orientation, 'orientation' );
28352793 }
@@ -2855,7 +2813,6 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
28552813 _vibrate ();
28562814 setState (() {
28572815 _hourMinuteMode.value = mode;
2858- _announceModeOnce ();
28592816 });
28602817 }
28612818
@@ -2877,37 +2834,6 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
28772834 });
28782835 }
28792836
2880- void _announceModeOnce () {
2881- if (_lastModeAnnounced.value == _hourMinuteMode.value) {
2882- // Already announced it.
2883- return ;
2884- }
2885-
2886- switch (_hourMinuteMode.value) {
2887- case _HourMinuteMode .hour:
2888- _announceToAccessibility (context, localizations.timePickerHourModeAnnouncement);
2889- case _HourMinuteMode .minute:
2890- _announceToAccessibility (context, localizations.timePickerMinuteModeAnnouncement);
2891- }
2892- _lastModeAnnounced.value = _hourMinuteMode.value;
2893- }
2894-
2895- void _announceInitialTimeOnce () {
2896- if (_announcedInitialTime.value) {
2897- return ;
2898- }
2899-
2900- final MaterialLocalizations localizations = MaterialLocalizations .of (context);
2901- _announceToAccessibility (
2902- context,
2903- localizations.formatTimeOfDay (
2904- _selectedTime.value,
2905- alwaysUse24HourFormat: MediaQuery .alwaysUse24HourFormatOf (context),
2906- ),
2907- );
2908- _announcedInitialTime.value = true ;
2909- }
2910-
29112837 void _handleTimeChanged (TimeOfDay value) {
29122838 _vibrate ();
29132839 setState (() {
@@ -2969,17 +2895,24 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
29692895 };
29702896 final Widget dial = Padding (
29712897 padding: dialPadding,
2972- child: ExcludeSemantics (
2973- child: SizedBox .fromSize (
2974- size: defaultTheme.dialSize,
2975- child: AspectRatio (
2976- aspectRatio: 1 ,
2977- child: _Dial (
2978- hourMinuteMode: _hourMinuteMode.value,
2979- hourDialType: hourMode,
2980- selectedTime: _selectedTime.value,
2981- onChanged: _handleTimeChanged,
2982- onHourSelected: _handleHourSelected,
2898+ child: Semantics (
2899+ label: switch (_hourMinuteMode.value) {
2900+ _HourMinuteMode .hour => localizations.timePickerHourModeAnnouncement,
2901+ _HourMinuteMode .minute => localizations.timePickerMinuteModeAnnouncement,
2902+ },
2903+ liveRegion: true ,
2904+ child: ExcludeSemantics (
2905+ child: SizedBox .fromSize (
2906+ size: defaultTheme.dialSize,
2907+ child: AspectRatio (
2908+ aspectRatio: 1 ,
2909+ child: _Dial (
2910+ hourMinuteMode: _hourMinuteMode.value,
2911+ hourDialType: hourMode,
2912+ selectedTime: _selectedTime.value,
2913+ onChanged: _handleTimeChanged,
2914+ onHourSelected: _handleHourSelected,
2915+ ),
29832916 ),
29842917 ),
29852918 ),
@@ -3233,10 +3166,6 @@ Future<TimeOfDay?> showTimePicker({
32333166 );
32343167}
32353168
3236- void _announceToAccessibility (BuildContext context, String message) {
3237- SemanticsService .announce (message, Directionality .of (context));
3238- }
3239-
32403169// An abstract base class for the M2 and M3 defaults below, so that their return
32413170// types can be non-nullable.
32423171abstract class _TimePickerDefaults extends TimePickerThemeData {
0 commit comments