diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index 78fd6f702ff..e3472a14bb6 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -267,10 +267,12 @@ class _TimePickerHeader extends StatelessWidget { ), ), Row( + textDirection: + timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm + ? TextDirection.rtl + : TextDirection.ltr, + spacing: 12, children: [ - if (hourDialType == _HourDialType.twelveHour && - timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) - const _DayPeriodControl(), Expanded( child: Row( // Hour/minutes should not change positions in RTL locales. @@ -282,11 +284,7 @@ class _TimePickerHeader extends StatelessWidget { ], ), ), - if (hourDialType == _HourDialType.twelveHour && - timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) ...[ - const SizedBox(width: 12), - const _DayPeriodControl(), - ], + if (hourDialType == _HourDialType.twelveHour) const _DayPeriodControl(), ], ), ], @@ -303,29 +301,24 @@ class _TimePickerHeader extends StatelessWidget { _TimePickerModel.defaultThemeOf(context).helpTextStyle, ), Column( + verticalDirection: + timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm + ? VerticalDirection.up + : VerticalDirection.down, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, + spacing: 12, children: [ - if (hourDialType == _HourDialType.twelveHour && - timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) - const _DayPeriodControl(), - Padding( - padding: EdgeInsets.only( - bottom: hourDialType == _HourDialType.twelveHour ? 12 : 0, - ), - child: Row( - // Hour/minutes should not change positions in RTL locales. - textDirection: TextDirection.ltr, - children: [ - const Expanded(child: _HourControl()), - _TimeSelectorSeparator(timeOfDayFormat: timeOfDayFormat), - const Expanded(child: _MinuteControl()), - ], - ), + Row( + // Hour/minutes should not change positions in RTL locales. + textDirection: TextDirection.ltr, + children: [ + const Expanded(child: _HourControl()), + _TimeSelectorSeparator(timeOfDayFormat: timeOfDayFormat), + const Expanded(child: _MinuteControl()), + ], ), - if (hourDialType == _HourDialType.twelveHour && - timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) - const _DayPeriodControl(), + if (hourDialType == _HourDialType.twelveHour) const _DayPeriodControl(), ], ), ], diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 2b1e755bfda..bf694a89367 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -10,6 +10,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import '../widgets/feedback_tester.dart'; @@ -2162,6 +2163,56 @@ void main() { expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0)); }); + + // Regression test for https://github.com/flutter/flutter/issues/162229. + testWidgets( + 'Time picker spacing between time control and day period control for locales using "a h:mm" pattern', + (WidgetTester tester) async { + addTearDown(tester.view.reset); + + final Finder dayPeriodControlFinder = find.byWidgetPredicate( + (Widget w) => '${w.runtimeType}' == '_DayPeriodControl', + ); + final Finder timeControlFinder = + find.ancestor(of: find.text('7'), matching: find.byType(Row)).first; + + // Render in portrait mode. + tester.view.physicalSize = const Size(800, 800.5); + tester.view.devicePixelRatio = 1; + await mediaQueryBoilerplate( + tester, + materialType: MaterialType.material3, + locale: const Locale('ko', 'KR'), + ); + + expect( + tester.getBottomLeft(timeControlFinder).dx - + tester.getBottomRight(dayPeriodControlFinder).dx, + 12, + ); + + // Dismiss the dialog. + final MaterialLocalizations materialLocalizations = MaterialLocalizations.of( + tester.element(find.byType(TextButton).first), + ); + await tester.tap(find.text(materialLocalizations.okButtonLabel)); + await tester.pumpAndSettle(); + + // Render in landscape mode. + tester.view.physicalSize = const Size(800.5, 800); + tester.view.devicePixelRatio = 1; + await mediaQueryBoilerplate( + tester, + materialType: MaterialType.material3, + locale: const Locale('ko', 'KR'), + ); + + expect( + tester.getTopLeft(timeControlFinder).dy - tester.getBottomLeft(dayPeriodControlFinder).dy, + 12, + ); + }, + ); } final Finder findDialPaint = find.descendant( @@ -2204,15 +2255,16 @@ Future mediaQueryBoilerplate( bool tapButton = true, required MaterialType materialType, Orientation? orientation, + Locale locale = const Locale('en', 'US'), }) async { await tester.pumpWidget( Theme( data: ThemeData(useMaterial3: materialType == MaterialType.material3), child: Localizations( - locale: const Locale('en', 'US'), + locale: locale, delegates: const >[ - DefaultMaterialLocalizations.delegate, - DefaultWidgetsLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, ], child: MediaQuery( data: MediaQueryData(