Skip to content

Commit

Permalink
Add cancelButtonStyle & confirmButtonStyle to the `DatePickerThem…
Browse files Browse the repository at this point in the history
…eData` (#132847)

fixes [Unable to adjust the color for "Cancel" and "Ok" button in datePicker dialog](flutter/flutter#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)
  • Loading branch information
TahaTesser authored Aug 30, 2023
1 parent 2f2a10d commit 1e770c3
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 12 deletions.
12 changes: 10 additions & 2 deletions dev/tools/gen_defaults/lib/date_picker_template.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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")};
Expand Down Expand Up @@ -231,8 +241,6 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData {
@override
TextStyle? get rangePickerHeaderHelpStyle => ${textStyle("md.comp.date-picker.modal.range-selection.month.subhead")};
}
''';
}
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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),
),
Expand Down
46 changes: 43 additions & 3 deletions packages/flutter/lib/src/material/date_picker_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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].
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
);
}

Expand Down Expand Up @@ -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),
);
}

Expand Down Expand Up @@ -459,6 +475,8 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionOverlayColor,
dividerColor,
inputDecorationTheme,
cancelButtonStyle,
confirmButtonStyle,
]);

@override
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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));
}
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -993,8 +1035,6 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {

@override
TextStyle? get rangePickerHeaderHelpStyle => _textTheme.titleSmall;


}

// END GENERATED TOKEN PROPERTIES - DatePicker
37 changes: 30 additions & 7 deletions packages/flutter/test/material/date_picker_theme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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)',
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 1e770c3

Please sign in to comment.