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 {
@override
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
Color? get shadowColor => ${colorOrTransparent("md.comp.date-picker.modal.container.shadow-color")};
......@@ -231,8 +241,6 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData {
@override
TextStyle? get rangePickerHeaderHelpStyle => ${textStyle("md.comp.date-picker.modal.range-selection.month.subhead")};
}
''';
}
......@@ -543,6 +543,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
spacing: 8,
children: <Widget>[
TextButton(
style: datePickerTheme.cancelButtonStyle ?? defaults.cancelButtonStyle,
onPressed: _handleCancel,
child: Text(widget.cancelText ?? (
useMaterial3
......@@ -551,6 +552,7 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
)),
),
TextButton(
style: datePickerTheme.confirmButtonStyle ?? defaults.confirmButtonStyle,
onPressed: _handleOk,
child: Text(widget.confirmText ?? localizations.okButtonLabel),
),
......
......@@ -7,10 +7,12 @@ import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'button_style.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'input_decorator.dart';
import 'material_state.dart';
import 'text_button.dart';
import 'text_theme.dart';
import 'theme.dart';
......@@ -70,6 +72,8 @@ class DatePickerThemeData with Diagnosticable {
this.rangeSelectionOverlayColor,
this.dividerColor,
this.inputDecorationTheme,
this.cancelButtonStyle,
this.confirmButtonStyle,
});
/// Overrides the default value of [Dialog.backgroundColor].
......@@ -294,6 +298,12 @@ class DatePickerThemeData with Diagnosticable {
/// If this is null, [ThemeData.inputDecorationTheme] is used instead.
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
/// new values.
DatePickerThemeData copyWith({
......@@ -331,6 +341,8 @@ class DatePickerThemeData with Diagnosticable {
MaterialStateProperty<Color?>? rangeSelectionOverlayColor,
Color? dividerColor,
InputDecorationTheme? inputDecorationTheme,
ButtonStyle? cancelButtonStyle,
ButtonStyle? confirmButtonStyle,
}) {
return DatePickerThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
......@@ -367,6 +379,8 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionOverlayColor: rangeSelectionOverlayColor ?? this.rangeSelectionOverlayColor,
dividerColor: dividerColor ?? this.dividerColor,
inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme,
cancelButtonStyle: cancelButtonStyle ?? this.cancelButtonStyle,
confirmButtonStyle: confirmButtonStyle ?? this.confirmButtonStyle,
);
}
......@@ -410,6 +424,8 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionOverlayColor: MaterialStateProperty.lerp<Color?>(a?.rangeSelectionOverlayColor, b?.rangeSelectionOverlayColor, t, Color.lerp),
dividerColor: Color.lerp(a?.dividerColor, b?.dividerColor, t),
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 {
rangeSelectionOverlayColor,
dividerColor,
inputDecorationTheme,
cancelButtonStyle,
confirmButtonStyle,
]);
@override
......@@ -500,7 +518,9 @@ class DatePickerThemeData with Diagnosticable {
&& other.rangeSelectionBackgroundColor == rangeSelectionBackgroundColor
&& other.rangeSelectionOverlayColor == rangeSelectionOverlayColor
&& other.dividerColor == dividerColor
&& other.inputDecorationTheme == inputDecorationTheme;
&& other.inputDecorationTheme == inputDecorationTheme
&& other.cancelButtonStyle == cancelButtonStyle
&& other.confirmButtonStyle == confirmButtonStyle;
}
@override
......@@ -540,6 +560,8 @@ class DatePickerThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('rangeSelectionOverlayColor', rangeSelectionOverlayColor, defaultValue: null));
properties.add(ColorProperty('dividerColor', dividerColor, 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 {
@override
Color? get headerBackgroundColor => _isDark ? _colors.surface : _colors.primary;
@override
ButtonStyle get cancelButtonStyle {
return TextButton.styleFrom();
}
@override
ButtonStyle get confirmButtonStyle {
return TextButton.styleFrom();
}
@override
Color? get headerForegroundColor => _isDark ? _colors.onSurface : _colors.onPrimary;
......@@ -818,6 +850,16 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {
@override
Color? get backgroundColor => _colors.surface;
@override
ButtonStyle get cancelButtonStyle {
return TextButton.styleFrom();
}
@override
ButtonStyle get confirmButtonStyle {
return TextButton.styleFrom();
}
@override
Color? get shadowColor => Colors.transparent;
......@@ -993,8 +1035,6 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {
@override
TextStyle? get rangePickerHeaderHelpStyle => _textTheme.titleSmall;
}
// END GENERATED TOKEN PROPERTIES - DatePicker
......@@ -46,7 +46,9 @@ void main() {
inputDecorationTheme: InputDecorationTheme(
fillColor: Color(0xffffff5f),
border: UnderlineInputBorder(),
)
),
cancelButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff6f))),
confirmButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff7f))),
);
Material findDialogMaterial(WidgetTester tester) {
......@@ -77,6 +79,10 @@ void main() {
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 narrowWindowSize = Size(1070.0, 1770.0);
......@@ -126,6 +132,8 @@ void main() {
expect(theme.rangeSelectionOverlayColor, null);
expect(theme.dividerColor, null);
expect(theme.inputDecorationTheme, null);
expect(theme.cancelButtonStyle, null);
expect(theme.confirmButtonStyle, null);
});
testWidgets('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async {
......@@ -201,6 +209,8 @@ void main() {
expect(m3.rangePickerHeaderHelpStyle, textTheme.titleSmall);
expect(m3.dividerColor, 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 {
......@@ -268,6 +278,8 @@ void main() {
expect(m2.rangePickerHeaderHelpStyle, textTheme.labelSmall);
expect(m2.dividerColor, 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 {
......@@ -292,9 +304,7 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(
description,
equalsIgnoringHashCodes(<String>[
expect(description, equalsIgnoringHashCodes(<String>[
'backgroundColor: Color(0xfffffff0)',
'elevation: 6.0',
'shadowColor: Color(0xfffffff1)',
......@@ -328,9 +338,10 @@ void main() {
'rangeSelectionBackgroundColor: Color(0xffffff2f)',
'rangeSelectionOverlayColor: MaterialStatePropertyAll(Color(0xffffff3f))',
'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 {
......@@ -426,6 +437,12 @@ void main() {
await gesture.moveTo(tester.getCenter(find.text('2024')));
await tester.pumpAndSettle();
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 {
......@@ -467,6 +484,12 @@ void main() {
final InputDecoration inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
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 {
......
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