From 1e770c3808e7a74fbef17a68f3dbd515f1d3c2a2 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Thu, 31 Aug 2023 00:16:13 +0300 Subject: [PATCH] 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
expand to view the code sample ```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), ), ), ); } } ```
### 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) --- .../lib/date_picker_template.dart | 12 ++++- .../flutter/lib/src/material/date_picker.dart | 2 + .../lib/src/material/date_picker_theme.dart | 46 +++++++++++++++++-- .../test/material/date_picker_theme_test.dart | 37 ++++++++++++--- 4 files changed, 85 insertions(+), 12 deletions(-) diff --git a/dev/tools/gen_defaults/lib/date_picker_template.dart b/dev/tools/gen_defaults/lib/date_picker_template.dart index 6f45fdf35f..e1c8836dee 100644 --- a/dev/tools/gen_defaults/lib/date_picker_template.dart +++ b/dev/tools/gen_defaults/lib/date_picker_template.dart @@ -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")}; - - } '''; } diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 6b495fa88b..88614ec1bd 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -543,6 +543,7 @@ class _DatePickerDialogState extends State with RestorationMix spacing: 8, children: [ TextButton( + style: datePickerTheme.cancelButtonStyle ?? defaults.cancelButtonStyle, onPressed: _handleCancel, child: Text(widget.cancelText ?? ( useMaterial3 @@ -551,6 +552,7 @@ class _DatePickerDialogState extends State with RestorationMix )), ), TextButton( + style: datePickerTheme.confirmButtonStyle ?? defaults.confirmButtonStyle, onPressed: _handleOk, child: Text(widget.confirmText ?? localizations.okButtonLabel), ), diff --git a/packages/flutter/lib/src/material/date_picker_theme.dart b/packages/flutter/lib/src/material/date_picker_theme.dart index a970ffbd5d..21ffc60ca5 100644 --- a/packages/flutter/lib/src/material/date_picker_theme.dart +++ b/packages/flutter/lib/src/material/date_picker_theme.dart @@ -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? 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(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>('rangeSelectionOverlayColor', rangeSelectionOverlayColor, defaultValue: null)); properties.add(ColorProperty('dividerColor', dividerColor, defaultValue: null)); properties.add(DiagnosticsProperty('inputDecorationTheme', inputDecorationTheme, defaultValue: null)); + properties.add(DiagnosticsProperty('cancelButtonStyle', cancelButtonStyle, defaultValue: null)); + properties.add(DiagnosticsProperty('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 diff --git a/packages/flutter/test/material/date_picker_theme_test.dart b/packages/flutter/test/material/date_picker_theme_test.dart index 4eaaff12d5..58bb87aed2 100644 --- a/packages/flutter/test/material/date_picker_theme_test.dart +++ b/packages/flutter/test/material/date_picker_theme_test.dart @@ -46,7 +46,9 @@ void main() { inputDecorationTheme: InputDecorationTheme( fillColor: Color(0xffffff5f), border: UnderlineInputBorder(), - ) + ), + cancelButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll(Color(0xffffff6f))), + confirmButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll(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(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([ + expect(description, equalsIgnoringHashCodes([ '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({}))); + + 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(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 {