Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/flutter/lib/src/material/date_picker_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:flutter/widgets.dart';

import 'color_scheme.dart';
import 'colors.dart';
import 'input_decorator.dart';
import 'material_state.dart';
import 'text_theme.dart';
import 'theme.dart';
Expand Down Expand Up @@ -68,6 +69,7 @@ class DatePickerThemeData with Diagnosticable {
this.rangeSelectionBackgroundColor,
this.rangeSelectionOverlayColor,
this.dividerColor,
this.inputDecorationTheme,
});

/// Overrides the default value of [Dialog.backgroundColor].
Expand Down Expand Up @@ -288,6 +290,10 @@ class DatePickerThemeData with Diagnosticable {
/// and vertical divider when the dialog is in landscape orientation.
final Color? dividerColor;

/// Overrides the [InputDatePickerFormField]'s input decoration theme.
/// If this is null, [ThemeData.inputDecorationTheme] is used instead.
final InputDecorationTheme? inputDecorationTheme;

/// Creates a copy of this object with the given fields replaced with the
/// new values.
DatePickerThemeData copyWith({
Expand Down Expand Up @@ -324,6 +330,7 @@ class DatePickerThemeData with Diagnosticable {
Color? rangeSelectionBackgroundColor,
MaterialStateProperty<Color?>? rangeSelectionOverlayColor,
Color? dividerColor,
InputDecorationTheme? inputDecorationTheme,
}) {
return DatePickerThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
Expand Down Expand Up @@ -359,6 +366,7 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionBackgroundColor: rangeSelectionBackgroundColor ?? this.rangeSelectionBackgroundColor,
rangeSelectionOverlayColor: rangeSelectionOverlayColor ?? this.rangeSelectionOverlayColor,
dividerColor: dividerColor ?? this.dividerColor,
inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme,
);
}

Expand Down Expand Up @@ -401,6 +409,7 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionBackgroundColor: Color.lerp(a?.rangeSelectionBackgroundColor, b?.rangeSelectionBackgroundColor, t),
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,
);
}

Expand Down Expand Up @@ -449,6 +458,7 @@ class DatePickerThemeData with Diagnosticable {
rangeSelectionBackgroundColor,
rangeSelectionOverlayColor,
dividerColor,
inputDecorationTheme,
]);

@override
Expand Down Expand Up @@ -489,7 +499,8 @@ class DatePickerThemeData with Diagnosticable {
&& other.rangePickerHeaderHelpStyle == rangePickerHeaderHelpStyle
&& other.rangeSelectionBackgroundColor == rangeSelectionBackgroundColor
&& other.rangeSelectionOverlayColor == rangeSelectionOverlayColor
&& other.dividerColor == dividerColor;
&& other.dividerColor == dividerColor
&& other.inputDecorationTheme == inputDecorationTheme;
}

@override
Expand Down Expand Up @@ -528,6 +539,7 @@ class DatePickerThemeData with Diagnosticable {
properties.add(ColorProperty('rangeSelectionBackgroundColor', rangeSelectionBackgroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('rangeSelectionOverlayColor', rangeSelectionOverlayColor, defaultValue: null));
properties.add(ColorProperty('dividerColor', dividerColor, defaultValue: null));
properties.add(DiagnosticsProperty<InputDecorationTheme>('inputDecorationTheme', inputDecorationTheme, defaultValue: null));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:flutter/widgets.dart';

import 'date.dart';
import 'date_picker_theme.dart';
import 'input_border.dart';
import 'input_decorator.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -248,16 +249,19 @@ class _InputDatePickerFormFieldState extends State<InputDatePickerFormField> {
final ThemeData theme = Theme.of(context);
final bool useMaterial3 = theme.useMaterial3;
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final DatePickerThemeData datePickerTheme = theme.datePickerTheme;
final InputDecorationTheme inputTheme = theme.inputDecorationTheme;
final InputBorder inputBorder = inputTheme.border
final InputBorder effectiveInputBorder = datePickerTheme.inputDecorationTheme?.border
?? theme.inputDecorationTheme.border
?? (useMaterial3 ? const OutlineInputBorder() : const UnderlineInputBorder());

return TextFormField(
decoration: InputDecoration(
border: inputBorder,
filled: inputTheme.filled,
hintText: widget.fieldHintText ?? localizations.dateHelpText,
labelText: widget.fieldLabelText ?? localizations.dateInputLabel,
).applyDefaults(inputTheme
.merge(datePickerTheme.inputDecorationTheme)
.copyWith(border: effectiveInputBorder),
),
validator: _validateDate,
keyboardType: widget.keyboardType ?? TextInputType.datetime,
Expand Down
132 changes: 122 additions & 10 deletions packages/flutter/test/material/date_picker_theme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ void main() {
rangePickerHeaderHelpStyle: TextStyle(fontSize: 15),
rangeSelectionBackgroundColor: Color(0xffffff2f),
rangeSelectionOverlayColor: MaterialStatePropertyAll<Color>(Color(0xffffff3f)),
dividerColor: Color(0xffffff3f),
dividerColor: Color(0xffffff4f),
inputDecorationTheme: InputDecorationTheme(
fillColor: Color(0xffffff5f),
border: UnderlineInputBorder(),
)
);

Material findDialogMaterial(WidgetTester tester) {
Expand Down Expand Up @@ -119,6 +123,7 @@ void main() {
expect(theme.rangeSelectionBackgroundColor, null);
expect(theme.rangeSelectionOverlayColor, null);
expect(theme.dividerColor, null);
expect(theme.inputDecorationTheme, null);
});

testWidgets('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async {
Expand All @@ -129,7 +134,7 @@ void main() {

await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(useMaterial3: true),
theme: ThemeData(useMaterial3: true),
home: Builder(
builder: (BuildContext context) {
m3 = DatePickerTheme.defaults(context);
Expand Down Expand Up @@ -193,6 +198,7 @@ void main() {
expect(m3.rangePickerHeaderHeadlineStyle, textTheme.titleLarge);
expect(m3.rangePickerHeaderHelpStyle, textTheme.titleSmall);
expect(m3.dividerColor, null);
expect(m3.inputDecorationTheme, null);
});

testWidgets('DatePickerTheme.defaults M2 defaults', (WidgetTester tester) async {
Expand All @@ -203,7 +209,7 @@ void main() {

await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(useMaterial3: false),
theme: ThemeData(useMaterial3: false),
home: Builder(
builder: (BuildContext context) {
m2 = DatePickerTheme.defaults(context);
Expand Down Expand Up @@ -258,6 +264,8 @@ void main() {
expect(m2.rangePickerHeaderForegroundColor, colorScheme.onPrimary);
expect(m2.rangePickerHeaderHeadlineStyle, textTheme.headlineSmall);
expect(m2.rangePickerHeaderHelpStyle, textTheme.labelSmall);
expect(m2.dividerColor, null);
expect(m2.inputDecorationTheme, null);
});

testWidgets('Default DatePickerThemeData debugFillProperties', (WidgetTester tester) async {
Expand All @@ -282,7 +290,9 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();

expect(description, <String>[
expect(
description,
equalsIgnoringHashCodes(<String>[
'backgroundColor: Color(0xfffffff0)',
'elevation: 6.0',
'shadowColor: Color(0xfffffff1)',
Expand Down Expand Up @@ -315,15 +325,18 @@ void main() {
'rangePickerHeaderHelpStyle: TextStyle(inherit: true, size: 15.0)',
'rangeSelectionBackgroundColor: Color(0xffffff2f)',
'rangeSelectionOverlayColor: MaterialStatePropertyAll(Color(0xffffff3f))',
'dividerColor: Color(0xffffff3f)',
]);
'dividerColor: Color(0xffffff4f)',
'inputDecorationTheme: InputDecorationTheme#00000(fillColor: Color(0xffffff5f), border: UnderlineInputBorder())'
]),
);
});

testWidgets('DatePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async {
testWidgets('DatePickerDialog uses ThemeData datePicker theme (calendar mode)', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(useMaterial3: true).copyWith(
theme: ThemeData(
datePickerTheme: datePickerTheme,
useMaterial3: true,
),
home: Directionality(
textDirection: TextDirection.ltr,
Expand Down Expand Up @@ -398,11 +411,53 @@ void main() {
expect(year2023Decoration.border?.bottom.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}));
});

testWidgets('DatePickerDialog uses ThemeData datePicker theme (input mode)', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
datePickerTheme: datePickerTheme,
useMaterial3: true,
),
home: Directionality(
textDirection: TextDirection.ltr,
child: Material(
child: Center(
child: DatePickerDialog(
initialEntryMode: DatePickerEntryMode.input,
initialDate: DateTime(2023, DateTime.january, 25),
firstDate: DateTime(2022),
lastDate: DateTime(2024, DateTime.december, 31),
currentDate: DateTime(2023, DateTime.january, 24),
),
),
),
),
),
);

final Material material = findDialogMaterial(tester);
expect(material.color, datePickerTheme.backgroundColor);
expect(material.elevation, datePickerTheme.elevation);
expect(material.shadowColor, datePickerTheme.shadowColor);
expect(material.surfaceTintColor, datePickerTheme.surfaceTintColor);
expect(material.shape, datePickerTheme.shape);

final Text selectDate = tester.widget<Text>(find.text('Select date'));
final Material headerMaterial = findHeaderMaterial(tester, 'Select date');
expect(selectDate.style?.color, datePickerTheme.headerForegroundColor);
expect(selectDate.style?.fontSize, datePickerTheme.headerHelpStyle?.fontSize);
expect(headerMaterial.color, datePickerTheme.headerBackgroundColor);

final InputDecoration inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
expect(inputDecoration.fillColor, datePickerTheme.inputDecorationTheme?.fillColor);
});

testWidgets('DateRangePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(useMaterial3: true).copyWith(
theme: ThemeData(
datePickerTheme: datePickerTheme,
useMaterial3: true,
),
home: Directionality(
textDirection: TextDirection.ltr,
Expand Down Expand Up @@ -450,8 +505,9 @@ void main() {
addTearDown(tester.view.reset);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(useMaterial3: true).copyWith(
theme: ThemeData(
datePickerTheme: datePickerTheme,
useMaterial3: true,
),
home: Directionality(
textDirection: TextDirection.ltr,
Expand Down Expand Up @@ -482,4 +538,60 @@ void main() {
final Divider horizontalDivider = tester.widget(find.byType(Divider));
expect(horizontalDivider.color, datePickerTheme.dividerColor);
});

testWidgets(
'DatePicker uses ThemeData.inputDecorationTheme properties '
'which are null in DatePickerThemeData.inputDecorationTheme',
(WidgetTester tester) async {

Widget buildWidget({
InputDecorationTheme? inputDecorationTheme,
DatePickerThemeData? datePickerTheme,
}) {
return MaterialApp(
theme: ThemeData(
useMaterial3: true,
inputDecorationTheme: inputDecorationTheme,
datePickerTheme: datePickerTheme,
),
home: Directionality(
textDirection: TextDirection.ltr,
child: Material(
child: Center(
child: DatePickerDialog(
initialEntryMode: DatePickerEntryMode.input,
initialDate: DateTime(2023, DateTime.january, 25),
firstDate: DateTime(2022),
lastDate: DateTime(2024, DateTime.december, 31),
currentDate: DateTime(2023, DateTime.january, 24),
),
),
),
),
);
}

// Test DatePicker with DatePickerThemeData.inputDecorationTheme.
await tester.pumpWidget(buildWidget(
inputDecorationTheme: const InputDecorationTheme(filled: true),
datePickerTheme: datePickerTheme,
));
InputDecoration inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
expect(inputDecoration.fillColor, datePickerTheme.inputDecorationTheme!.fillColor);
expect(inputDecoration.border , datePickerTheme.inputDecorationTheme!.border);

// Test DatePicker with ThemeData.inputDecorationTheme.
await tester.pumpWidget(buildWidget(
inputDecorationTheme: const InputDecorationTheme(
filled: true,
fillColor: Color(0xFF00FF00),
border: OutlineInputBorder(),
),
));
await tester.pumpAndSettle();

inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
expect(inputDecoration.fillColor, const Color(0xFF00FF00));
expect(inputDecoration.border , const OutlineInputBorder());
});
}