Fix TimePicker hour and minute inputs are resized on error (#154008)

Fixes [Defining inputDecorationTheme in TimePickerThemeData Causes Misalignment of Hour and Minute Input Boxes](https://github.com/flutter/flutter/issues/153549)

### Code sample

<details>
<summary>expand to view the code sample</summary> 

```dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        timePickerTheme: const TimePickerThemeData(
          inputDecorationTheme: InputDecorationTheme(),
        ),
      ),
      home: Scaffold(
        body: Center(
          child: Builder(builder: (BuildContext context) {
            return ElevatedButton(
              onPressed: () async {
                await showTimePicker(
                  context: context,
                  initialEntryMode: TimePickerEntryMode.input,
                  initialTime: TimeOfDay.now(),
                );
              },
              child: const Text('Show Time Picker'),
            );
          }),
        ),
      ),
    );
  }
}

```

</details>

### Before

<img width="578" alt="Screenshot 2024-08-23 at 16 49 25" src="https://github.com/user-attachments/assets/f5da2495-551e-4110-85ea-120323cd38d2">

### After

<img width="578" alt="Screenshot 2024-08-23 at 16 51 03" src="https://github.com/user-attachments/assets/80224a10-e9d2-46d1-b2eb-f16358699744">
This commit is contained in:
Taha Tesser 2024-08-23 20:15:36 +03:00 committed by GitHub
parent b2de4dfc2b
commit d7b092e42d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 99 additions and 4 deletions

View File

@ -331,7 +331,7 @@ class _${blockName}DefaultsM3 extends _TimePickerDefaults {
// TODO(rami-a): Remove this workaround once
// https://github.com/flutter/flutter/issues/54104
// is fixed.
errorStyle: const TextStyle(fontSize: 0, height: 0),
errorStyle: const TextStyle(fontSize: 0),
);
}

View File

@ -2047,7 +2047,14 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora
final bool alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context);
final InputDecorationTheme inputDecorationTheme = timePickerTheme.inputDecorationTheme ?? defaultTheme.inputDecorationTheme;
InputDecoration inputDecoration = const InputDecoration().applyDefaults(inputDecorationTheme);
InputDecoration inputDecoration = InputDecoration(
// Prevent the error text from appearing when
// timePickerTheme.inputDecorationTheme is used.
// TODO(tahatesser): Remove this workaround once
// https://github.com/flutter/flutter/issues/54104
// is fixed.
errorStyle: defaultTheme.inputDecorationTheme.errorStyle,
).applyDefaults(inputDecorationTheme);
// Remove the hint text when focused because the centered cursor
// appears odd above the hint text.
final String? hintText = focusNode.hasFocus ? null : _formattedValue;
@ -3341,7 +3348,7 @@ class _TimePickerDefaultsM2 extends _TimePickerDefaults {
// TODO(rami-a): Remove this workaround once
// https://github.com/flutter/flutter/issues/54104
// is fixed.
errorStyle: const TextStyle(fontSize: 0, height: 0),
errorStyle: const TextStyle(fontSize: 0, height: 1),
);
}
@ -3676,7 +3683,7 @@ class _TimePickerDefaultsM3 extends _TimePickerDefaults {
// TODO(rami-a): Remove this workaround once
// https://github.com/flutter/flutter/issues/54104
// is fixed.
errorStyle: const TextStyle(fontSize: 0, height: 0),
errorStyle: const TextStyle(fontSize: 0),
);
}

View File

@ -19,10 +19,18 @@ void main() {
const String okString = 'OK';
const String amString = 'AM';
const String pmString = 'PM';
Material getMaterialFromDialog(WidgetTester tester) {
return tester.widget<Material>(find.descendant(of: find.byType(Dialog), matching: find.byType(Material)).first);
}
Finder findBorderPainter() {
return find.descendant(
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_BorderContainer'),
matching: find.byWidgetPredicate((Widget w) => w is CustomPaint),
);
}
testWidgets('Material2 - Dialog size - dial mode', (WidgetTester tester) async {
addTearDown(tester.view.reset);
@ -2013,6 +2021,43 @@ void main() {
expect(paragraph.text.style!.color, theme.colorScheme.onSurface);
expect(paragraph.text.style!.fontSize, 56.0);
});
// This is a regression test for https://github.com/flutter/flutter/issues/153549.
testWidgets('Time picker hour minute does not resize on error', (WidgetTester tester) async {
await startPicker(
entryMode: TimePickerEntryMode.input,
tester,
(TimeOfDay? value) { },
);
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0));
// Enter invalid hour.
await tester.enterText(find.byType(TextField).first, 'AB');
await tester.tap(find.text(okString));
await tester.pumpAndSettle();
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0));
});
// This is a regression test for https://github.com/flutter/flutter/issues/153549.
testWidgets('Material2 - Time picker hour minute does not resize on error', (WidgetTester tester) async {
await startPicker(
entryMode: TimePickerEntryMode.input,
tester,
(TimeOfDay? value) { },
materialType: MaterialType.material2,
);
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0));
// Enter invalid hour.
await tester.enterText(find.byType(TextField).first, 'AB');
await tester.tap(find.text(okString));
await tester.pumpAndSettle();
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0));
});
}
final Finder findDialPaint = find.descendant(

View File

@ -858,6 +858,42 @@ void main() {
expect(paragraph.text.style!.fontSize, 35.0);
expect(paragraph.text.style!.fontStyle, FontStyle.italic);
});
// This is a regression test for https://github.com/flutter/flutter/issues/153549.
testWidgets('Time picker hour minute does not resize on error', (WidgetTester tester) async {
final TimePickerThemeData timePickerTheme = _timePickerTheme(includeInputDecoration: true);
final ThemeData theme = ThemeData(timePickerTheme: timePickerTheme);
await tester.pumpWidget(_TimePickerLauncher(themeData: theme, entryMode: TimePickerEntryMode.input));
await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 72.0));
// Enter invalid hour.
await tester.enterText(find.byType(TextField).first, 'AB');
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 72.0));
});
// This is a regression test for https://github.com/flutter/flutter/issues/153549.
testWidgets('Material2 - Time picker hour minute does not resize on error', (WidgetTester tester) async {
final TimePickerThemeData timePickerTheme = _timePickerTheme(includeInputDecoration: true);
final ThemeData theme = ThemeData(timePickerTheme: timePickerTheme, useMaterial3: false);
await tester.pumpWidget(_TimePickerLauncher(themeData: theme, entryMode: TimePickerEntryMode.input));
await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0));
// Enter invalid hour.
await tester.enterText(find.byType(TextField).first, 'AB');
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(tester.getSize(findBorderPainter().first), const Size(96.0, 70.0));
});
}
final Color _selectedColor = Colors.green[100]!;
@ -970,3 +1006,10 @@ final Finder findDialPaint = find.descendant(
ButtonStyle _actionButtonStyle(WidgetTester tester, String text) {
return tester.widget<TextButton>(find.widgetWithText(TextButton, text)).style!;
}
Finder findBorderPainter() {
return find.descendant(
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_BorderContainer'),
matching: find.byWidgetPredicate((Widget w) => w is CustomPaint),
);
}