mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
modify toggle mode style with DatePickerTheme (#164102)
- Add properties to DatePickerThemeData: - `toggleModeStyle` allow customization on _DatePickerModeToggleButton TextStyle; - `toggleModeForegroundColor` allow changing the color of both button and Icon with consistency; - Compatible with existing code by the use of defaults; - Adjust test to cover new properties How to customize toggle mode style before: ```dart showDatePicker( context: context, firstDate: DateTime(2025), lastDate: DateTime(2026), builder: (context, child) => Theme( data: ThemeData.light().copyWith( textTheme: TextTheme(titleSmall: TextStyle(fontSize: 16)), datePickerTheme: DatePickerThemeData( dayStyle: TextStyle(fontSize: 12), ), ), child: child!, ), ); ``` Now: ```dart showDatePicker( context: context, firstDate: DateTime(2025), lastDate: DateTime(2026), builder: (context, child) => DatePickerTheme( data: DatePickerThemeData( toggleModeStyle: TextStyle(fontSize: 16), dayStyle: TextStyle(fontSize: 12), ), child: child!, ), ); ``` Ref #163866 Ref #160591 <!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
37e79eb90c
commit
dc7c2892d9
3
AUTHORS
3
AUTHORS
@ -132,4 +132,5 @@ dy0gu <support@dy0gu.com>
|
||||
Albert Wolszon <albert@wolszon.me>
|
||||
Mohammed Chahboun <m97.chahboun@gmail.com>
|
||||
Abdessalem Mohellebi <mohellebiabdessalem@gmail.com>
|
||||
Jin Jeongsu <jinjs.dev@gmail.com>
|
||||
Jin Jeongsu <jinjs.dev@gmail.com>
|
||||
Mairon Slusarz <maironlucaslusarz@gmail.com>
|
||||
|
@ -63,6 +63,14 @@ class _${blockName}DefaultsM3 extends DatePickerThemeData {
|
||||
@override
|
||||
Color? get backgroundColor => ${componentColor("md.comp.date-picker.modal.container")};
|
||||
|
||||
@override
|
||||
Color? get subHeaderForegroundColor => ${componentColor("md.comp.date-picker.modal.weekdays.label-text")}.withOpacity(0.60);
|
||||
|
||||
@override
|
||||
TextStyle? get toggleButtonTextStyle => ${textStyle("md.comp.date-picker.modal.range-selection.month.subhead")}?.apply(
|
||||
color: subHeaderForegroundColor,
|
||||
);
|
||||
|
||||
@override
|
||||
ButtonStyle get cancelButtonStyle {
|
||||
return TextButton.styleFrom();
|
||||
|
@ -13,7 +13,6 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'color_scheme.dart';
|
||||
import 'date.dart';
|
||||
import 'date_picker_theme.dart';
|
||||
import 'debug.dart';
|
||||
@ -24,7 +23,6 @@ import 'ink_decoration.dart';
|
||||
import 'ink_well.dart';
|
||||
import 'material_localizations.dart';
|
||||
import 'material_state.dart';
|
||||
import 'text_theme.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
const Duration _monthScrollDuration = Duration(milliseconds: 200);
|
||||
@ -450,9 +448,16 @@ class _DatePickerModeToggleButtonState extends State<_DatePickerModeToggleButton
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
||||
final TextTheme textTheme = Theme.of(context).textTheme;
|
||||
final Color controlColor = colorScheme.onSurface.withOpacity(0.60);
|
||||
final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
|
||||
final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
|
||||
final TextStyle? buttonTextStyle =
|
||||
datePickerTheme.toggleButtonTextStyle ?? defaults.toggleButtonTextStyle;
|
||||
final Color? subHeaderForegroundColor =
|
||||
datePickerTheme.subHeaderForegroundColor ?? defaults.subHeaderForegroundColor;
|
||||
final Color? buttonTextColor =
|
||||
datePickerTheme.toggleButtonTextStyle?.color ??
|
||||
datePickerTheme.subHeaderForegroundColor ??
|
||||
defaults.toggleButtonTextStyle?.color;
|
||||
|
||||
return SizedBox(
|
||||
height: _subHeaderHeight,
|
||||
@ -477,12 +482,12 @@ class _DatePickerModeToggleButtonState extends State<_DatePickerModeToggleButton
|
||||
child: Text(
|
||||
widget.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: textTheme.titleSmall?.copyWith(color: controlColor),
|
||||
style: buttonTextStyle?.apply(color: buttonTextColor),
|
||||
),
|
||||
),
|
||||
RotationTransition(
|
||||
turns: _controller,
|
||||
child: Icon(Icons.arrow_drop_down, color: controlColor),
|
||||
child: Icon(Icons.arrow_drop_down, color: subHeaderForegroundColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -826,7 +831,9 @@ class _MonthPickerState extends State<_MonthPicker> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color controlColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.60);
|
||||
final Color? subHeaderForegroundColor =
|
||||
DatePickerTheme.of(context).subHeaderForegroundColor ??
|
||||
DatePickerTheme.defaults(context).subHeaderForegroundColor;
|
||||
|
||||
return Semantics(
|
||||
container: true,
|
||||
@ -842,13 +849,13 @@ class _MonthPickerState extends State<_MonthPicker> {
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.chevron_left),
|
||||
color: controlColor,
|
||||
color: subHeaderForegroundColor,
|
||||
tooltip: _isDisplayingFirstMonth ? null : _localizations.previousMonthTooltip,
|
||||
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
color: controlColor,
|
||||
color: subHeaderForegroundColor,
|
||||
tooltip: _isDisplayingLastMonth ? null : _localizations.nextMonthTooltip,
|
||||
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth,
|
||||
),
|
||||
|
@ -84,6 +84,8 @@ class DatePickerThemeData with Diagnosticable {
|
||||
this.cancelButtonStyle,
|
||||
this.confirmButtonStyle,
|
||||
this.locale,
|
||||
this.toggleButtonTextStyle,
|
||||
this.subHeaderForegroundColor,
|
||||
});
|
||||
|
||||
/// Overrides the default value of [Dialog.backgroundColor].
|
||||
@ -373,6 +375,16 @@ class DatePickerThemeData with Diagnosticable {
|
||||
/// picker. It defaults to the ambient locale provided by [Localizations].
|
||||
final Locale? locale;
|
||||
|
||||
/// Overrides the default text style used for the text of toggle mode button.
|
||||
///
|
||||
/// If no [TextStyle.color] is given, [subHeaderForegroundColor] will be used.
|
||||
final TextStyle? toggleButtonTextStyle;
|
||||
|
||||
/// Overrides the default color used for text labels and icons of sub header foreground.
|
||||
///
|
||||
/// This is used in [TextStyle.color] property of [toggleButtonTextStyle] if no color is given.
|
||||
final Color? subHeaderForegroundColor;
|
||||
|
||||
/// Creates a copy of this object with the given fields replaced with the
|
||||
/// new values.
|
||||
DatePickerThemeData copyWith({
|
||||
@ -415,6 +427,8 @@ class DatePickerThemeData with Diagnosticable {
|
||||
ButtonStyle? cancelButtonStyle,
|
||||
ButtonStyle? confirmButtonStyle,
|
||||
Locale? locale,
|
||||
TextStyle? toggleButtonTextStyle,
|
||||
Color? subHeaderForegroundColor,
|
||||
}) {
|
||||
return DatePickerThemeData(
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
@ -460,6 +474,8 @@ class DatePickerThemeData with Diagnosticable {
|
||||
cancelButtonStyle: cancelButtonStyle ?? this.cancelButtonStyle,
|
||||
confirmButtonStyle: confirmButtonStyle ?? this.confirmButtonStyle,
|
||||
locale: locale ?? this.locale,
|
||||
toggleButtonTextStyle: toggleButtonTextStyle ?? this.toggleButtonTextStyle,
|
||||
subHeaderForegroundColor: subHeaderForegroundColor ?? this.subHeaderForegroundColor,
|
||||
);
|
||||
}
|
||||
|
||||
@ -591,6 +607,12 @@ class DatePickerThemeData with Diagnosticable {
|
||||
cancelButtonStyle: ButtonStyle.lerp(a?.cancelButtonStyle, b?.cancelButtonStyle, t),
|
||||
confirmButtonStyle: ButtonStyle.lerp(a?.confirmButtonStyle, b?.confirmButtonStyle, t),
|
||||
locale: t < 0.5 ? a?.locale : b?.locale,
|
||||
toggleButtonTextStyle: TextStyle.lerp(a?.toggleButtonTextStyle, b?.toggleButtonTextStyle, t),
|
||||
subHeaderForegroundColor: Color.lerp(
|
||||
a?.subHeaderForegroundColor,
|
||||
b?.subHeaderForegroundColor,
|
||||
t,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -645,6 +667,8 @@ class DatePickerThemeData with Diagnosticable {
|
||||
cancelButtonStyle,
|
||||
confirmButtonStyle,
|
||||
locale,
|
||||
toggleButtonTextStyle,
|
||||
subHeaderForegroundColor,
|
||||
]);
|
||||
|
||||
@override
|
||||
@ -691,7 +715,9 @@ class DatePickerThemeData with Diagnosticable {
|
||||
other.inputDecorationTheme == inputDecorationTheme &&
|
||||
other.cancelButtonStyle == cancelButtonStyle &&
|
||||
other.confirmButtonStyle == confirmButtonStyle &&
|
||||
other.locale == locale;
|
||||
other.locale == locale &&
|
||||
other.toggleButtonTextStyle == toggleButtonTextStyle &&
|
||||
other.subHeaderForegroundColor == subHeaderForegroundColor;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -872,6 +898,16 @@ class DatePickerThemeData with Diagnosticable {
|
||||
),
|
||||
);
|
||||
properties.add(DiagnosticsProperty<Locale>('locale', locale, defaultValue: null));
|
||||
properties.add(
|
||||
DiagnosticsProperty<TextStyle>(
|
||||
'toggleButtonTextStyle',
|
||||
toggleButtonTextStyle,
|
||||
defaultValue: null,
|
||||
),
|
||||
);
|
||||
properties.add(
|
||||
ColorProperty('subHeaderForegroundColor', subHeaderForegroundColor, defaultValue: null),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -988,6 +1024,13 @@ class _DatePickerDefaultsM2 extends DatePickerThemeData {
|
||||
@override
|
||||
Color? get headerBackgroundColor => _isDark ? _colors.surface : _colors.primary;
|
||||
|
||||
@override
|
||||
Color? get subHeaderForegroundColor => _colors.onSurface.withOpacity(0.60);
|
||||
|
||||
@override
|
||||
TextStyle? get toggleButtonTextStyle =>
|
||||
_textTheme.titleSmall?.apply(color: subHeaderForegroundColor);
|
||||
|
||||
@override
|
||||
ButtonStyle get cancelButtonStyle {
|
||||
return TextButton.styleFrom();
|
||||
@ -1132,7 +1175,6 @@ class _DatePickerDefaultsM2 extends DatePickerThemeData {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES - DatePicker
|
||||
|
||||
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||
@ -1162,6 +1204,14 @@ class _DatePickerDefaultsM3 extends DatePickerThemeData {
|
||||
@override
|
||||
Color? get backgroundColor => _colors.surfaceContainerHigh;
|
||||
|
||||
@override
|
||||
Color? get subHeaderForegroundColor => _colors.onSurface.withOpacity(0.60);
|
||||
|
||||
@override
|
||||
TextStyle? get toggleButtonTextStyle => _textTheme.titleSmall?.apply(
|
||||
color: subHeaderForegroundColor,
|
||||
);
|
||||
|
||||
@override
|
||||
ButtonStyle get cancelButtonStyle {
|
||||
return TextButton.styleFrom();
|
||||
|
@ -56,6 +56,8 @@ void main() {
|
||||
foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff7f)),
|
||||
),
|
||||
locale: Locale('en'),
|
||||
subHeaderForegroundColor: Color(0xffffff8f),
|
||||
toggleButtonTextStyle: TextStyle(fontSize: 13),
|
||||
);
|
||||
|
||||
Material findDialogMaterial(WidgetTester tester) {
|
||||
@ -141,6 +143,8 @@ void main() {
|
||||
expect(theme.cancelButtonStyle, null);
|
||||
expect(theme.confirmButtonStyle, null);
|
||||
expect(theme.locale, null);
|
||||
expect(theme.subHeaderForegroundColor, null);
|
||||
expect(theme.toggleButtonTextStyle, null);
|
||||
});
|
||||
|
||||
testWidgets('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async {
|
||||
@ -296,6 +300,11 @@ void main() {
|
||||
equalsIgnoringHashCodes(TextButton.styleFrom().toString()),
|
||||
);
|
||||
expect(m3.locale, null);
|
||||
expect(m3.subHeaderForegroundColor, colorScheme.onSurface.withOpacity(0.60));
|
||||
expect(
|
||||
m3.toggleButtonTextStyle,
|
||||
textTheme.titleSmall?.apply(color: m3.subHeaderForegroundColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('DatePickerTheme.defaults M2 defaults', (WidgetTester tester) async {
|
||||
@ -451,6 +460,11 @@ void main() {
|
||||
);
|
||||
expect(m2.locale, null);
|
||||
expect(m2.yearShape?.resolve(<MaterialState>{}), const StadiumBorder());
|
||||
expect(m2.subHeaderForegroundColor, colorScheme.onSurface.withOpacity(0.60));
|
||||
expect(
|
||||
m2.toggleButtonTextStyle,
|
||||
textTheme.titleSmall?.apply(color: m2.subHeaderForegroundColor),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Default DatePickerThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
@ -519,6 +533,8 @@ void main() {
|
||||
'cancelButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(${const Color(0xffffff6f)}))',
|
||||
'confirmButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(${const Color(0xffffff7f)}))',
|
||||
'locale: en',
|
||||
'toggleButtonTextStyle: TextStyle(inherit: true, size: 13.0)',
|
||||
'subHeaderForegroundColor: ${const Color(0xffffff8f)}',
|
||||
]),
|
||||
);
|
||||
});
|
||||
@ -591,6 +607,13 @@ void main() {
|
||||
);
|
||||
expect(day24Shape.side.width, datePickerTheme.todayBorder?.width);
|
||||
|
||||
// Test the toggle mode button style.
|
||||
final Text january2023 = tester.widget<Text>(find.text('January 2023'));
|
||||
expect(january2023.style?.fontSize, datePickerTheme.toggleButtonTextStyle?.fontSize);
|
||||
expect(january2023.style?.color, datePickerTheme.subHeaderForegroundColor);
|
||||
final Icon arrowIcon = tester.widget<Icon>(find.byIcon(Icons.arrow_drop_down));
|
||||
expect(arrowIcon.color, datePickerTheme.subHeaderForegroundColor);
|
||||
|
||||
// Test the day overlay color.
|
||||
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere(
|
||||
(RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures',
|
||||
@ -1312,4 +1335,58 @@ void main() {
|
||||
datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Toggle button uses DatePickerTheme.toggleButtonTextStyle.color when it is defined', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
const Color toggleButtonTextColor = Color(0xff00ff00);
|
||||
const Color subHeaderForegroundColor = Color(0xffff0000);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
datePickerTheme: const DatePickerThemeData(
|
||||
toggleButtonTextStyle: TextStyle(color: toggleButtonTextColor),
|
||||
subHeaderForegroundColor: subHeaderForegroundColor,
|
||||
),
|
||||
),
|
||||
home: DatePickerDialog(
|
||||
initialDate: DateTime(2023, DateTime.january, 25),
|
||||
firstDate: DateTime(2022),
|
||||
lastDate: DateTime(2024, DateTime.december, 31),
|
||||
currentDate: DateTime(2023, DateTime.january, 24),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Text toggleButtonText = tester.widget(find.text('January 2023'));
|
||||
expect(toggleButtonText.style?.color, toggleButtonTextColor);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Toggle button uses DatePickerTheme.subHeaderForegroundColor when DatePickerTheme.toggleButtonTextStyle.color is not defined',
|
||||
(WidgetTester tester) async {
|
||||
const Color subHeaderForegroundColor = Color(0xffff0000);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
datePickerTheme: const DatePickerThemeData(
|
||||
toggleButtonTextStyle: TextStyle(),
|
||||
subHeaderForegroundColor: subHeaderForegroundColor,
|
||||
),
|
||||
),
|
||||
home: DatePickerDialog(
|
||||
initialDate: DateTime(2023, DateTime.january, 25),
|
||||
firstDate: DateTime(2022),
|
||||
lastDate: DateTime(2024, DateTime.december, 31),
|
||||
currentDate: DateTime(2023, DateTime.january, 24),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Text toggleButtonText = tester.widget(find.text('January 2023'));
|
||||
expect(toggleButtonText.style?.color, subHeaderForegroundColor);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user