Unverified Commit 1e770c38 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add `cancelButtonStyle` & `confirmButtonStyle` to the `DatePickerThemeData` (#132847)

fixes [Unable to adjust the color for "Cancel" and "Ok" button in datePicker dialog](https://github.com/flutter/flutter/issues/127739)

### 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(
        useMaterial3: true,
        datePickerTheme: DatePickerThemeData(
          cancelButtonStyle: TextButton.styleFrom(
            shape: const RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(16)),
              side: BorderSide(color: Colors.red),
            ),
            backgroundColor: Colors.white,
            foregroundColor: Colors.red,
            elevation: 3,
            shadowColor: Colors.red,
          ),
          confirmButtonStyle: TextButton.styleFrom(
            shape: const RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(16)),
            ),
            backgroundColor: Colors.green[700],
            foregroundColor: Colors.white,
            elevation: 3,
            shadowColor: Colors.green[700],
          ),
        ),
      ),
      home: const Example(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: DatePickerDialog(
          initialDate: DateTime.now(),
          firstDate: DateTime(2020),
          lastDate: DateTime(2030),
        ),
      ),
    );
  }
}
``` 

</details>

### Before 

Not possible to customize action buttons from the `DatePickerThemeData`.

### After 

![Screenshot 2023-08-18 at 16 42 00](https://github.com/flutter/flutter/assets/48603081/4ec01e93-c661-491d-9253-d687da8b76f3)
parent 2f2a10d5
...@@ -56,6 +56,16 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData { ...@@ -56,6 +56,16 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData {
@override @override
Color? get backgroundColor => ${componentColor("md.comp.date-picker.modal.container")}; Color? get backgroundColor => ${componentColor("md.comp.date-picker.modal.container")};
@override
ButtonStyle get cancelButtonStyle {
return TextButton.styleFrom();
}
@override
ButtonStyle get confirmButtonStyle {
return TextButton.styleFrom();
}
@override @override
Color? get shadowColor => ${colorOrTransparent("md.comp.date-picker.modal.container.shadow-color")}; Color? get shadowColor => ${colorOrTransparent("md.comp.date-picker.modal.container.shadow-color")};
...@@ -231,8 +241,6 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData { ...@@ -231,8 +241,6 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData {
@override @override
TextStyle? get rangePickerHeaderHelpStyle => ${textStyle("md.comp.date-picker.modal.range-selection.month.subhead")}; TextStyle? get rangePickerHeaderHelpStyle => ${textStyle("md.comp.date-picker.modal.range-selection.month.subhead")};
} }
'''; ''';
} }
...@@ -543,6 +543,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix ...@@ -543,6 +543,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
spacing: 8, spacing: 8,
children: <Widget>[ children: <Widget>[
TextButton( TextButton(
style: datePickerTheme.cancelButtonStyle ?? defaults.cancelButtonStyle,
onPressed: _handleCancel, onPressed: _handleCancel,
child: Text(widget.cancelText ?? ( child: Text(widget.cancelText ?? (
useMaterial3 useMaterial3
...@@ -551,6 +552,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix ...@@ -551,6 +552,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
)), )),
), ),
TextButton( TextButton(
style: datePickerTheme.confirmButtonStyle ?? defaults.confirmButtonStyle,
onPressed: _handleOk, onPressed: _handleOk,
child: Text(widget.confirmText ?? localizations.okButtonLabel), child: Text(widget.confirmText ?? localizations.okButtonLabel),
), ),
......
...@@ -7,10 +7,12 @@ import 'dart:ui' show lerpDouble; ...@@ -7,10 +7,12 @@ import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_style.dart';
import 'color_scheme.dart'; import 'color_scheme.dart';
import 'colors.dart'; import 'colors.dart';
import 'input_decorator.dart'; import 'input_decorator.dart';
import 'material_state.dart'; import 'material_state.dart';
import 'text_button.dart';
import 'text_theme.dart'; import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -70,6 +72,8 @@ class DatePickerThemeData with Diagnosticable { ...@@ -70,6 +72,8 @@ class DatePickerThemeData with Diagnosticable {
this.rangeSelectionOverlayColor, this.rangeSelectionOverlayColor,
this.dividerColor, this.dividerColor,
this.inputDecorationTheme, this.inputDecorationTheme,
this.cancelButtonStyle,
this.confirmButtonStyle,
}); });
/// Overrides the default value of [Dialog.backgroundColor]. /// Overrides the default value of [Dialog.backgroundColor].
...@@ -294,6 +298,12 @@ class DatePickerThemeData with Diagnosticable { ...@@ -294,6 +298,12 @@ class DatePickerThemeData with Diagnosticable {
/// If this is null, [ThemeData.inputDecorationTheme] is used instead. /// If this is null, [ThemeData.inputDecorationTheme] is used instead.
final InputDecorationTheme? inputDecorationTheme; final InputDecorationTheme? inputDecorationTheme;
/// Overrides the default style of the cancel button of a [DatePickerDialog].
final ButtonStyle? cancelButtonStyle;
/// Overrrides the default style of the confirm (OK) button of a [DatePickerDialog].
final ButtonStyle? confirmButtonStyle;
/// Creates a copy of this object with the given fields replaced with the /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
DatePickerThemeData copyWith({ DatePickerThemeData copyWith({
...@@ -331,6 +341,8 @@ class DatePickerThemeData with Diagnosticable { ...@@ -331,6 +341,8 @@ class DatePickerThemeData with Diagnosticable {
MaterialStateProperty<Color?>? rangeSelectionOverlayColor, MaterialStateProperty<Color?>? rangeSelectionOverlayColor,
Color? dividerColor, Color? dividerColor,
InputDecorationTheme? inputDecorationTheme, InputDecorationTheme? inputDecorationTheme,
ButtonStyle? cancelButtonStyle,
ButtonStyle? confirmButtonStyle,
}) { }) {
return DatePickerThemeData( return DatePickerThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
...@@ -367,6 +379,8 @@ class DatePickerThemeData with Diagnosticable { ...@@ -367,6 +379,8 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionOverlayColor: rangeSelectionOverlayColor ?? this.rangeSelectionOverlayColor, rangeSelectionOverlayColor: rangeSelectionOverlayColor ?? this.rangeSelectionOverlayColor,
dividerColor: dividerColor ?? this.dividerColor, dividerColor: dividerColor ?? this.dividerColor,
inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme, inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme,
cancelButtonStyle: cancelButtonStyle ?? this.cancelButtonStyle,
confirmButtonStyle: confirmButtonStyle ?? this.confirmButtonStyle,
); );
} }
...@@ -410,6 +424,8 @@ class DatePickerThemeData with Diagnosticable { ...@@ -410,6 +424,8 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionOverlayColor: MaterialStateProperty.lerp<Color?>(a?.rangeSelectionOverlayColor, b?.rangeSelectionOverlayColor, t, Color.lerp), rangeSelectionOverlayColor: MaterialStateProperty.lerp<Color?>(a?.rangeSelectionOverlayColor, b?.rangeSelectionOverlayColor, t, Color.lerp),
dividerColor: Color.lerp(a?.dividerColor, b?.dividerColor, t), dividerColor: Color.lerp(a?.dividerColor, b?.dividerColor, t),
inputDecorationTheme: t < 0.5 ? a?.inputDecorationTheme : b?.inputDecorationTheme, inputDecorationTheme: t < 0.5 ? a?.inputDecorationTheme : b?.inputDecorationTheme,
cancelButtonStyle: ButtonStyle.lerp(a?.cancelButtonStyle, b?.cancelButtonStyle, t),
confirmButtonStyle: ButtonStyle.lerp(a?.confirmButtonStyle, b?.confirmButtonStyle, t),
); );
} }
...@@ -459,6 +475,8 @@ class DatePickerThemeData with Diagnosticable { ...@@ -459,6 +475,8 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionOverlayColor, rangeSelectionOverlayColor,
dividerColor, dividerColor,
inputDecorationTheme, inputDecorationTheme,
cancelButtonStyle,
confirmButtonStyle,
]); ]);
@override @override
...@@ -500,7 +518,9 @@ class DatePickerThemeData with Diagnosticable { ...@@ -500,7 +518,9 @@ class DatePickerThemeData with Diagnosticable {
&& other.rangeSelectionBackgroundColor == rangeSelectionBackgroundColor && other.rangeSelectionBackgroundColor == rangeSelectionBackgroundColor
&& other.rangeSelectionOverlayColor == rangeSelectionOverlayColor && other.rangeSelectionOverlayColor == rangeSelectionOverlayColor
&& other.dividerColor == dividerColor && other.dividerColor == dividerColor
&& other.inputDecorationTheme == inputDecorationTheme; && other.inputDecorationTheme == inputDecorationTheme
&& other.cancelButtonStyle == cancelButtonStyle
&& other.confirmButtonStyle == confirmButtonStyle;
} }
@override @override
...@@ -540,6 +560,8 @@ class DatePickerThemeData with Diagnosticable { ...@@ -540,6 +560,8 @@ class DatePickerThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('rangeSelectionOverlayColor', rangeSelectionOverlayColor, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('rangeSelectionOverlayColor', rangeSelectionOverlayColor, defaultValue: null));
properties.add(ColorProperty('dividerColor', dividerColor, defaultValue: null)); properties.add(ColorProperty('dividerColor', dividerColor, defaultValue: null));
properties.add(DiagnosticsProperty<InputDecorationTheme>('inputDecorationTheme', inputDecorationTheme, defaultValue: null)); properties.add(DiagnosticsProperty<InputDecorationTheme>('inputDecorationTheme', inputDecorationTheme, defaultValue: null));
properties.add(DiagnosticsProperty<ButtonStyle>('cancelButtonStyle', cancelButtonStyle, defaultValue: null));
properties.add(DiagnosticsProperty<ButtonStyle>('confirmButtonStyle', confirmButtonStyle, defaultValue: null));
} }
} }
...@@ -658,6 +680,16 @@ class _DatePickerDefaultsM2 extends DatePickerThemeData { ...@@ -658,6 +680,16 @@ class _DatePickerDefaultsM2 extends DatePickerThemeData {
@override @override
Color? get headerBackgroundColor => _isDark ? _colors.surface : _colors.primary; Color? get headerBackgroundColor => _isDark ? _colors.surface : _colors.primary;
@override
ButtonStyle get cancelButtonStyle {
return TextButton.styleFrom();
}
@override
ButtonStyle get confirmButtonStyle {
return TextButton.styleFrom();
}
@override @override
Color? get headerForegroundColor => _isDark ? _colors.onSurface : _colors.onPrimary; Color? get headerForegroundColor => _isDark ? _colors.onSurface : _colors.onPrimary;
...@@ -818,6 +850,16 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData { ...@@ -818,6 +850,16 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {
@override @override
Color? get backgroundColor => _colors.surface; Color? get backgroundColor => _colors.surface;
@override
ButtonStyle get cancelButtonStyle {
return TextButton.styleFrom();
}
@override
ButtonStyle get confirmButtonStyle {
return TextButton.styleFrom();
}
@override @override
Color? get shadowColor => Colors.transparent; Color? get shadowColor => Colors.transparent;
...@@ -993,8 +1035,6 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData { ...@@ -993,8 +1035,6 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {
@override @override
TextStyle? get rangePickerHeaderHelpStyle => _textTheme.titleSmall; TextStyle? get rangePickerHeaderHelpStyle => _textTheme.titleSmall;
} }
// END GENERATED TOKEN PROPERTIES - DatePicker // END GENERATED TOKEN PROPERTIES - DatePicker
...@@ -46,7 +46,9 @@ void main() { ...@@ -46,7 +46,9 @@ void main() {
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
fillColor: Color(0xffffff5f), fillColor: Color(0xffffff5f),
border: UnderlineInputBorder(), border: UnderlineInputBorder(),
) ),
cancelButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff6f))),
confirmButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff7f))),
); );
Material findDialogMaterial(WidgetTester tester) { Material findDialogMaterial(WidgetTester tester) {
...@@ -77,6 +79,10 @@ void main() { ...@@ -77,6 +79,10 @@ void main() {
return container.decoration as BoxDecoration?; return container.decoration as BoxDecoration?;
} }
ButtonStyle actionButtonStyle(WidgetTester tester, String text) {
return tester.widget<TextButton>(find.widgetWithText(TextButton, text)).style!;
}
const Size wideWindowSize = Size(1920.0, 1080.0); const Size wideWindowSize = Size(1920.0, 1080.0);
const Size narrowWindowSize = Size(1070.0, 1770.0); const Size narrowWindowSize = Size(1070.0, 1770.0);
...@@ -126,6 +132,8 @@ void main() { ...@@ -126,6 +132,8 @@ void main() {
expect(theme.rangeSelectionOverlayColor, null); expect(theme.rangeSelectionOverlayColor, null);
expect(theme.dividerColor, null); expect(theme.dividerColor, null);
expect(theme.inputDecorationTheme, null); expect(theme.inputDecorationTheme, null);
expect(theme.cancelButtonStyle, null);
expect(theme.confirmButtonStyle, null);
}); });
testWidgets('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async { testWidgets('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async {
...@@ -201,6 +209,8 @@ void main() { ...@@ -201,6 +209,8 @@ void main() {
expect(m3.rangePickerHeaderHelpStyle, textTheme.titleSmall); expect(m3.rangePickerHeaderHelpStyle, textTheme.titleSmall);
expect(m3.dividerColor, null); expect(m3.dividerColor, null);
expect(m3.inputDecorationTheme, null); expect(m3.inputDecorationTheme, null);
expect(m3.cancelButtonStyle.toString(), equalsIgnoringHashCodes(TextButton.styleFrom().toString()));
expect(m3.confirmButtonStyle.toString(), equalsIgnoringHashCodes(TextButton.styleFrom().toString()));
}); });
testWidgets('DatePickerTheme.defaults M2 defaults', (WidgetTester tester) async { testWidgets('DatePickerTheme.defaults M2 defaults', (WidgetTester tester) async {
...@@ -268,6 +278,8 @@ void main() { ...@@ -268,6 +278,8 @@ void main() {
expect(m2.rangePickerHeaderHelpStyle, textTheme.labelSmall); expect(m2.rangePickerHeaderHelpStyle, textTheme.labelSmall);
expect(m2.dividerColor, null); expect(m2.dividerColor, null);
expect(m2.inputDecorationTheme, null); expect(m2.inputDecorationTheme, null);
expect(m2.cancelButtonStyle.toString(), equalsIgnoringHashCodes(TextButton.styleFrom().toString()));
expect(m2.confirmButtonStyle.toString(), equalsIgnoringHashCodes(TextButton.styleFrom().toString()));
}); });
testWidgets('Default DatePickerThemeData debugFillProperties', (WidgetTester tester) async { testWidgets('Default DatePickerThemeData debugFillProperties', (WidgetTester tester) async {
...@@ -292,9 +304,7 @@ void main() { ...@@ -292,9 +304,7 @@ void main() {
.map((DiagnosticsNode node) => node.toString()) .map((DiagnosticsNode node) => node.toString())
.toList(); .toList();
expect( expect(description, equalsIgnoringHashCodes(<String>[
description,
equalsIgnoringHashCodes(<String>[
'backgroundColor: Color(0xfffffff0)', 'backgroundColor: Color(0xfffffff0)',
'elevation: 6.0', 'elevation: 6.0',
'shadowColor: Color(0xfffffff1)', 'shadowColor: Color(0xfffffff1)',
...@@ -328,9 +338,10 @@ void main() { ...@@ -328,9 +338,10 @@ void main() {
'rangeSelectionBackgroundColor: Color(0xffffff2f)', 'rangeSelectionBackgroundColor: Color(0xffffff2f)',
'rangeSelectionOverlayColor: MaterialStatePropertyAll(Color(0xffffff3f))', 'rangeSelectionOverlayColor: MaterialStatePropertyAll(Color(0xffffff3f))',
'dividerColor: Color(0xffffff4f)', 'dividerColor: Color(0xffffff4f)',
'inputDecorationTheme: InputDecorationTheme#00000(fillColor: Color(0xffffff5f), border: UnderlineInputBorder())' 'inputDecorationTheme: InputDecorationTheme#00000(fillColor: Color(0xffffff5f), border: UnderlineInputBorder())',
]), 'cancelButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xffffff6f)))',
); 'confirmButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xffffff7f)))'
]));
}); });
testWidgets('DatePickerDialog uses ThemeData datePicker theme (calendar mode)', (WidgetTester tester) async { testWidgets('DatePickerDialog uses ThemeData datePicker theme (calendar mode)', (WidgetTester tester) async {
...@@ -426,6 +437,12 @@ void main() { ...@@ -426,6 +437,12 @@ void main() {
await gesture.moveTo(tester.getCenter(find.text('2024'))); await gesture.moveTo(tester.getCenter(find.text('2024')));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(inkFeatures, paints..rect(color: datePickerTheme.yearOverlayColor?.resolve(<MaterialState>{}))); expect(inkFeatures, paints..rect(color: datePickerTheme.yearOverlayColor?.resolve(<MaterialState>{})));
final ButtonStyle cancelButtonStyle = actionButtonStyle(tester, 'Cancel');
expect(cancelButtonStyle.toString(), equalsIgnoringHashCodes(datePickerTheme.cancelButtonStyle.toString()));
final ButtonStyle confirmButtonStyle = actionButtonStyle(tester, 'OK');
expect(confirmButtonStyle.toString(), equalsIgnoringHashCodes(datePickerTheme.confirmButtonStyle.toString()));
}); });
testWidgets('DatePickerDialog uses ThemeData datePicker theme (input mode)', (WidgetTester tester) async { testWidgets('DatePickerDialog uses ThemeData datePicker theme (input mode)', (WidgetTester tester) async {
...@@ -467,6 +484,12 @@ void main() { ...@@ -467,6 +484,12 @@ void main() {
final InputDecoration inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!; final InputDecoration inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
expect(inputDecoration.fillColor, datePickerTheme.inputDecorationTheme?.fillColor); expect(inputDecoration.fillColor, datePickerTheme.inputDecorationTheme?.fillColor);
final ButtonStyle cancelButtonStyle = actionButtonStyle(tester, 'Cancel');
expect(cancelButtonStyle.toString(), equalsIgnoringHashCodes(datePickerTheme.cancelButtonStyle.toString()));
final ButtonStyle confirmButtonStyle = actionButtonStyle(tester, 'OK');
expect(confirmButtonStyle.toString(), equalsIgnoringHashCodes(datePickerTheme.confirmButtonStyle.toString()));
}); });
testWidgets('DateRangePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async { testWidgets('DateRangePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment