1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/foundation.dart';
6import 'package:flutter/gestures.dart';
7import 'package:flutter/material.dart';
8import 'package:flutter/services.dart';
9import 'package:flutter_test/flutter_test.dart';
10
11void main() {
12 const DatePickerThemeData datePickerTheme = DatePickerThemeData(
13 backgroundColor: Color(0xfffffff0),
14 elevation: 6,
15 shadowColor: Color(0xfffffff1),
16 surfaceTintColor: Color(0xfffffff2),
17 shape: RoundedRectangleBorder(),
18 headerBackgroundColor: Color(0xfffffff3),
19 headerForegroundColor: Color(0xfffffff4),
20 headerHeadlineStyle: TextStyle(fontSize: 10),
21 headerHelpStyle: TextStyle(fontSize: 11),
22 weekdayStyle: TextStyle(fontSize: 12),
23 dayStyle: TextStyle(fontSize: 13),
24 dayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff5)),
25 dayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff6)),
26 dayOverlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff7)),
27 dayShape: MaterialStatePropertyAll<OutlinedBorder>(RoundedRectangleBorder()),
28 todayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff8)),
29 todayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff9)),
30 todayBorder: BorderSide(width: 3),
31 yearStyle: TextStyle(fontSize: 13),
32 yearForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffffa)),
33 yearBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffffb)),
34 yearOverlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffffc)),
35 yearShape: MaterialStatePropertyAll<OutlinedBorder>(RoundedRectangleBorder()),
36 rangePickerBackgroundColor: Color(0xfffffffd),
37 rangePickerElevation: 7,
38 rangePickerShadowColor: Color(0xfffffffe),
39 rangePickerSurfaceTintColor: Color(0xffffffff),
40 rangePickerShape: RoundedRectangleBorder(),
41 rangePickerHeaderBackgroundColor: Color(0xffffff0f),
42 rangePickerHeaderForegroundColor: Color(0xffffff1f),
43 rangePickerHeaderHeadlineStyle: TextStyle(fontSize: 14),
44 rangePickerHeaderHelpStyle: TextStyle(fontSize: 15),
45 rangeSelectionBackgroundColor: Color(0xffffff2f),
46 rangeSelectionOverlayColor: MaterialStatePropertyAll<Color>(Color(0xffffff3f)),
47 dividerColor: Color(0xffffff4f),
48 inputDecorationTheme: InputDecorationTheme(
49 fillColor: Color(0xffffff5f),
50 border: UnderlineInputBorder(),
51 ),
52 cancelButtonStyle: ButtonStyle(
53 foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff6f)),
54 ),
55 confirmButtonStyle: ButtonStyle(
56 foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff7f)),
57 ),
58 locale: Locale('en'),
59 subHeaderForegroundColor: Color(0xffffff8f),
60 toggleButtonTextStyle: TextStyle(fontSize: 13),
61 );
62
63 Material findDialogMaterial(WidgetTester tester) {
64 return tester.widget<Material>(
65 find.descendant(of: find.byType(Dialog), matching: find.byType(Material)).first,
66 );
67 }
68
69 Material findHeaderMaterial(WidgetTester tester, String text) {
70 return tester.widget<Material>(
71 find.ancestor(of: find.text(text), matching: find.byType(Material)).first,
72 );
73 }
74
75 ShapeDecoration? findTextDecoration(WidgetTester tester, String date) {
76 final Container container = tester.widget<Container>(
77 find.ancestor(of: find.text(date), matching: find.byType(Container)).first,
78 );
79 return container.decoration as ShapeDecoration?;
80 }
81
82 ShapeDecoration? findDayDecoration(WidgetTester tester, String day) {
83 return tester
84 .widget<Ink>(find.ancestor(of: find.text(day), matching: find.byType(Ink)))
85 .decoration
86 as ShapeDecoration?;
87 }
88
89 ButtonStyle actionButtonStyle(WidgetTester tester, String text) {
90 return tester.widget<TextButton>(find.widgetWithText(TextButton, text)).style!;
91 }
92
93 const Size wideWindowSize = Size(1920.0, 1080.0);
94 const Size narrowWindowSize = Size(1070.0, 1770.0);
95
96 test('DatePickerThemeData copyWith, ==, hashCode basics', () {
97 expect(const DatePickerThemeData(), const DatePickerThemeData().copyWith());
98 expect(const DatePickerThemeData().hashCode, const DatePickerThemeData().copyWith().hashCode);
99 });
100
101 test('DatePickerThemeData lerp special cases', () {
102 const DatePickerThemeData data = DatePickerThemeData();
103 expect(identical(DatePickerThemeData.lerp(data, data, 0.5), data), true);
104 });
105
106 test('DatePickerThemeData defaults', () {
107 const DatePickerThemeData theme = DatePickerThemeData();
108 expect(theme.backgroundColor, null);
109 expect(theme.elevation, null);
110 expect(theme.shadowColor, null);
111 expect(theme.surfaceTintColor, null);
112 expect(theme.shape, null);
113 expect(theme.headerBackgroundColor, null);
114 expect(theme.headerForegroundColor, null);
115 expect(theme.headerHeadlineStyle, null);
116 expect(theme.headerHelpStyle, null);
117 expect(theme.weekdayStyle, null);
118 expect(theme.dayStyle, null);
119 expect(theme.dayForegroundColor, null);
120 expect(theme.dayBackgroundColor, null);
121 expect(theme.dayOverlayColor, null);
122 expect(theme.dayShape, null);
123 expect(theme.todayForegroundColor, null);
124 expect(theme.todayBackgroundColor, null);
125 expect(theme.todayBorder, null);
126 expect(theme.yearStyle, null);
127 expect(theme.yearForegroundColor, null);
128 expect(theme.yearBackgroundColor, null);
129 expect(theme.yearOverlayColor, null);
130 expect(theme.rangePickerBackgroundColor, null);
131 expect(theme.rangePickerElevation, null);
132 expect(theme.rangePickerShadowColor, null);
133 expect(theme.rangePickerSurfaceTintColor, null);
134 expect(theme.rangePickerShape, null);
135 expect(theme.rangePickerHeaderBackgroundColor, null);
136 expect(theme.rangePickerHeaderForegroundColor, null);
137 expect(theme.rangePickerHeaderHeadlineStyle, null);
138 expect(theme.rangePickerHeaderHelpStyle, null);
139 expect(theme.rangeSelectionBackgroundColor, null);
140 expect(theme.rangeSelectionOverlayColor, null);
141 expect(theme.dividerColor, null);
142 expect(theme.inputDecorationTheme, null);
143 expect(theme.cancelButtonStyle, null);
144 expect(theme.confirmButtonStyle, null);
145 expect(theme.locale, null);
146 expect(theme.subHeaderForegroundColor, null);
147 expect(theme.toggleButtonTextStyle, null);
148 });
149
150 testWidgets('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async {
151 late final DatePickerThemeData m3; // M3 Defaults
152 late final ThemeData theme;
153 late final ColorScheme colorScheme;
154 late final TextTheme textTheme;
155
156 await tester.pumpWidget(
157 MaterialApp(
158 home: Builder(
159 builder: (BuildContext context) {
160 m3 = DatePickerTheme.defaults(context);
161 theme = Theme.of(context);
162 colorScheme = theme.colorScheme;
163 textTheme = theme.textTheme;
164 return Container();
165 },
166 ),
167 ),
168 );
169
170 expect(m3.backgroundColor, colorScheme.surfaceContainerHigh);
171 expect(m3.elevation, 6);
172 expect(m3.shadowColor, const Color(0x00000000)); // Colors.transparent
173 expect(m3.surfaceTintColor, Colors.transparent);
174 expect(m3.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)));
175 expect(m3.headerBackgroundColor, const Color(0x00000000)); // Colors.transparent
176 expect(m3.headerForegroundColor, colorScheme.onSurfaceVariant);
177 expect(m3.headerHeadlineStyle, textTheme.headlineLarge);
178 expect(m3.headerHelpStyle, textTheme.labelLarge);
179 expect(m3.weekdayStyle, textTheme.bodyLarge?.apply(color: colorScheme.onSurface));
180 expect(m3.dayStyle, textTheme.bodyLarge);
181 expect(m3.dayForegroundColor?.resolve(<MaterialState>{}), colorScheme.onSurface);
182 expect(
183 m3.dayForegroundColor?.resolve(<MaterialState>{MaterialState.selected}),
184 colorScheme.onPrimary,
185 );
186 expect(
187 m3.dayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}),
188 colorScheme.onSurface.withOpacity(0.38),
189 );
190 expect(m3.dayBackgroundColor?.resolve(<MaterialState>{}), null);
191 expect(
192 m3.dayBackgroundColor?.resolve(<MaterialState>{MaterialState.selected}),
193 colorScheme.primary,
194 );
195 expect(m3.dayOverlayColor?.resolve(<MaterialState>{}), null);
196 expect(
197 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}),
198 colorScheme.onPrimary.withOpacity(0.08),
199 );
200 expect(
201 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}),
202 colorScheme.onPrimary.withOpacity(0.1),
203 );
204 expect(
205 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}),
206 colorScheme.onSurfaceVariant.withOpacity(0.08),
207 );
208 expect(
209 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.focused}),
210 colorScheme.onSurfaceVariant.withOpacity(0.1),
211 );
212 expect(
213 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}),
214 colorScheme.onSurfaceVariant.withOpacity(0.1),
215 );
216 expect(
217 m3.dayOverlayColor?.resolve(<MaterialState>{
218 MaterialState.selected,
219 MaterialState.hovered,
220 MaterialState.focused,
221 }),
222 colorScheme.onPrimary.withOpacity(0.08),
223 );
224 expect(
225 m3.dayOverlayColor?.resolve(<MaterialState>{
226 MaterialState.selected,
227 MaterialState.hovered,
228 MaterialState.pressed,
229 }),
230 colorScheme.onPrimary.withOpacity(0.1),
231 );
232 expect(
233 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.focused}),
234 colorScheme.onSurfaceVariant.withOpacity(0.08),
235 );
236 expect(
237 m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.pressed}),
238 colorScheme.onSurfaceVariant.withOpacity(0.1),
239 );
240 expect(m3.dayShape?.resolve(<MaterialState>{}), const CircleBorder());
241 expect(m3.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary);
242 expect(
243 m3.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}),
244 colorScheme.primary.withOpacity(0.38),
245 );
246 expect(m3.todayBorder, BorderSide(color: colorScheme.primary));
247 expect(m3.yearStyle, textTheme.bodyLarge);
248 expect(m3.yearForegroundColor?.resolve(<MaterialState>{}), colorScheme.onSurfaceVariant);
249 expect(
250 m3.yearForegroundColor?.resolve(<MaterialState>{MaterialState.selected}),
251 colorScheme.onPrimary,
252 );
253 expect(
254 m3.yearForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}),
255 colorScheme.onSurfaceVariant.withOpacity(0.38),
256 );
257 expect(m3.yearBackgroundColor?.resolve(<MaterialState>{}), null);
258 expect(
259 m3.yearBackgroundColor?.resolve(<MaterialState>{MaterialState.selected}),
260 colorScheme.primary,
261 );
262 expect(m3.yearOverlayColor?.resolve(<MaterialState>{}), null);
263 expect(
264 m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}),
265 colorScheme.onPrimary.withOpacity(0.08),
266 );
267 expect(
268 m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}),
269 colorScheme.onPrimary.withOpacity(0.1),
270 );
271 expect(
272 m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}),
273 colorScheme.onSurfaceVariant.withOpacity(0.08),
274 );
275 expect(
276 m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.focused}),
277 colorScheme.onSurfaceVariant.withOpacity(0.1),
278 );
279 expect(
280 m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}),
281 colorScheme.onSurfaceVariant.withOpacity(0.1),
282 );
283 expect(m3.rangePickerElevation, 0);
284 expect(m3.rangePickerShape, const RoundedRectangleBorder());
285 expect(m3.rangePickerShadowColor, Colors.transparent);
286 expect(m3.rangePickerSurfaceTintColor, Colors.transparent);
287 expect(m3.rangeSelectionOverlayColor?.resolve(<MaterialState>{}), null);
288 expect(m3.rangePickerHeaderBackgroundColor, Colors.transparent);
289 expect(m3.rangePickerHeaderForegroundColor, colorScheme.onSurfaceVariant);
290 expect(m3.rangePickerHeaderHeadlineStyle, textTheme.titleLarge);
291 expect(m3.rangePickerHeaderHelpStyle, textTheme.titleSmall);
292 expect(m3.dividerColor, null);
293 expect(m3.inputDecorationTheme, null);
294 expect(
295 m3.cancelButtonStyle.toString(),
296 equalsIgnoringHashCodes(TextButton.styleFrom().toString()),
297 );
298 expect(
299 m3.confirmButtonStyle.toString(),
300 equalsIgnoringHashCodes(TextButton.styleFrom().toString()),
301 );
302 expect(m3.locale, null);
303 expect(m3.subHeaderForegroundColor, colorScheme.onSurface.withOpacity(0.60));
304 expect(
305 m3.toggleButtonTextStyle,
306 textTheme.titleSmall?.apply(color: m3.subHeaderForegroundColor),
307 );
308 });
309
310 testWidgets('DatePickerTheme.defaults M2 defaults', (WidgetTester tester) async {
311 late final DatePickerThemeData m2; // M2 defaults
312 late final ThemeData theme;
313 late final ColorScheme colorScheme;
314 late final TextTheme textTheme;
315
316 await tester.pumpWidget(
317 MaterialApp(
318 theme: ThemeData(useMaterial3: false),
319 home: Builder(
320 builder: (BuildContext context) {
321 m2 = DatePickerTheme.defaults(context);
322 theme = Theme.of(context);
323 colorScheme = theme.colorScheme;
324 textTheme = theme.textTheme;
325 return Container();
326 },
327 ),
328 ),
329 );
330
331 expect(m2.elevation, 24);
332 expect(
333 m2.shape,
334 const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))),
335 );
336 expect(m2.headerBackgroundColor, colorScheme.primary);
337 expect(m2.headerForegroundColor, colorScheme.onPrimary);
338 expect(m2.headerHeadlineStyle, textTheme.headlineSmall);
339 expect(m2.headerHelpStyle, textTheme.labelSmall);
340 expect(
341 m2.weekdayStyle,
342 textTheme.bodySmall?.apply(color: colorScheme.onSurface.withOpacity(0.60)),
343 );
344 expect(m2.dayStyle, textTheme.bodySmall);
345 expect(m2.dayForegroundColor?.resolve(<MaterialState>{}), colorScheme.onSurface);
346 expect(
347 m2.dayForegroundColor?.resolve(<MaterialState>{MaterialState.selected}),
348 colorScheme.onPrimary,
349 );
350 expect(
351 m2.dayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}),
352 colorScheme.onSurface.withOpacity(0.38),
353 );
354 expect(m2.dayBackgroundColor?.resolve(<MaterialState>{}), null);
355 expect(
356 m2.dayBackgroundColor?.resolve(<MaterialState>{MaterialState.selected}),
357 colorScheme.primary,
358 );
359 expect(m2.dayOverlayColor?.resolve(<MaterialState>{}), null);
360 expect(
361 m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}),
362 colorScheme.onPrimary.withOpacity(0.08),
363 );
364 expect(
365 m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}),
366 colorScheme.onPrimary.withOpacity(0.12),
367 );
368 expect(
369 m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.pressed}),
370 colorScheme.onPrimary.withOpacity(0.38),
371 );
372 expect(
373 m2.dayOverlayColor?.resolve(<MaterialState>{
374 MaterialState.selected,
375 MaterialState.hovered,
376 MaterialState.focused,
377 }),
378 colorScheme.onPrimary.withOpacity(0.08),
379 );
380 expect(
381 m2.dayOverlayColor?.resolve(<MaterialState>{
382 MaterialState.selected,
383 MaterialState.hovered,
384 MaterialState.pressed,
385 }),
386 colorScheme.onPrimary.withOpacity(0.38),
387 );
388 expect(
389 m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}),
390 colorScheme.onSurfaceVariant.withOpacity(0.08),
391 );
392 expect(
393 m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.focused}),
394 colorScheme.onSurfaceVariant.withOpacity(0.12),
395 );
396 expect(
397 m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}),
398 colorScheme.onSurfaceVariant.withOpacity(0.12),
399 );
400 expect(m2.dayShape?.resolve(<MaterialState>{}), const CircleBorder());
401 expect(m2.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary);
402 expect(
403 m2.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}),
404 colorScheme.onSurface.withOpacity(0.38),
405 );
406 expect(m2.todayBorder, BorderSide(color: colorScheme.primary));
407 expect(m2.yearStyle, textTheme.bodyLarge);
408 expect(m2.rangePickerBackgroundColor, colorScheme.surface);
409 expect(m2.rangePickerElevation, 0);
410 expect(m2.rangePickerShape, const RoundedRectangleBorder());
411 expect(m2.rangePickerShadowColor, Colors.transparent);
412 expect(m2.rangePickerSurfaceTintColor, Colors.transparent);
413 expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{}), null);
414 expect(
415 m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{
416 MaterialState.selected,
417 MaterialState.hovered,
418 }),
419 colorScheme.onPrimary.withOpacity(0.08),
420 );
421 expect(
422 m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{
423 MaterialState.selected,
424 MaterialState.focused,
425 }),
426 colorScheme.onPrimary.withOpacity(0.12),
427 );
428 expect(
429 m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{
430 MaterialState.selected,
431 MaterialState.pressed,
432 }),
433 colorScheme.onPrimary.withOpacity(0.38),
434 );
435 expect(
436 m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}),
437 colorScheme.onSurfaceVariant.withOpacity(0.08),
438 );
439 expect(
440 m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.focused}),
441 colorScheme.onSurfaceVariant.withOpacity(0.12),
442 );
443 expect(
444 m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}),
445 colorScheme.onSurfaceVariant.withOpacity(0.12),
446 );
447 expect(m2.rangePickerHeaderBackgroundColor, colorScheme.primary);
448 expect(m2.rangePickerHeaderForegroundColor, colorScheme.onPrimary);
449 expect(m2.rangePickerHeaderHeadlineStyle, textTheme.headlineSmall);
450 expect(m2.rangePickerHeaderHelpStyle, textTheme.labelSmall);
451 expect(m2.dividerColor, null);
452 expect(m2.inputDecorationTheme, null);
453 expect(
454 m2.cancelButtonStyle.toString(),
455 equalsIgnoringHashCodes(TextButton.styleFrom().toString()),
456 );
457 expect(
458 m2.confirmButtonStyle.toString(),
459 equalsIgnoringHashCodes(TextButton.styleFrom().toString()),
460 );
461 expect(m2.locale, null);
462 expect(m2.yearShape?.resolve(<MaterialState>{}), const StadiumBorder());
463 expect(m2.subHeaderForegroundColor, colorScheme.onSurface.withOpacity(0.60));
464 expect(
465 m2.toggleButtonTextStyle,
466 textTheme.titleSmall?.apply(color: m2.subHeaderForegroundColor),
467 );
468 });
469
470 testWidgets('Default DatePickerThemeData debugFillProperties', (WidgetTester tester) async {
471 final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
472 const DatePickerThemeData().debugFillProperties(builder);
473
474 final List<String> description = builder.properties
475 .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
476 .map((DiagnosticsNode node) => node.toString())
477 .toList();
478
479 expect(description, <String>[]);
480 });
481
482 testWidgets('DatePickerThemeData implements debugFillProperties', (WidgetTester tester) async {
483 final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
484
485 datePickerTheme.debugFillProperties(builder);
486
487 final List<String> description = builder.properties
488 .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
489 .map((DiagnosticsNode node) => node.toString())
490 .toList();
491
492 expect(
493 description,
494 equalsIgnoringHashCodes(<String>[
495 'backgroundColor: ${const Color(0xfffffff0)}',
496 'elevation: 6.0',
497 'shadowColor: ${const Color(0xfffffff1)}',
498 'surfaceTintColor: ${const Color(0xfffffff2)}',
499 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)',
500 'headerBackgroundColor: ${const Color(0xfffffff3)}',
501 'headerForegroundColor: ${const Color(0xfffffff4)}',
502 'headerHeadlineStyle: TextStyle(inherit: true, size: 10.0)',
503 'headerHelpStyle: TextStyle(inherit: true, size: 11.0)',
504 'weekDayStyle: TextStyle(inherit: true, size: 12.0)',
505 'dayStyle: TextStyle(inherit: true, size: 13.0)',
506 'dayForegroundColor: WidgetStatePropertyAll(${const Color(0xfffffff5)})',
507 'dayBackgroundColor: WidgetStatePropertyAll(${const Color(0xfffffff6)})',
508 'dayOverlayColor: WidgetStatePropertyAll(${const Color(0xfffffff7)})',
509 'dayShape: WidgetStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero))',
510 'todayForegroundColor: WidgetStatePropertyAll(${const Color(0xfffffff8)})',
511 'todayBackgroundColor: WidgetStatePropertyAll(${const Color(0xfffffff9)})',
512 'todayBorder: BorderSide(width: 3.0)',
513 'yearStyle: TextStyle(inherit: true, size: 13.0)',
514 'yearForegroundColor: WidgetStatePropertyAll(${const Color(0xfffffffa)})',
515 'yearBackgroundColor: WidgetStatePropertyAll(${const Color(0xfffffffb)})',
516 'yearOverlayColor: WidgetStatePropertyAll(${const Color(0xfffffffc)})',
517 'yearShape: WidgetStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero))',
518 'rangePickerBackgroundColor: ${const Color(0xfffffffd)}',
519 'rangePickerElevation: 7.0',
520 'rangePickerShadowColor: ${const Color(0xfffffffe)}',
521 'rangePickerSurfaceTintColor: ${const Color(0xffffffff)}',
522 'rangePickerShape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)',
523 'rangePickerHeaderBackgroundColor: ${const Color(0xffffff0f)}',
524 'rangePickerHeaderForegroundColor: ${const Color(0xffffff1f)}',
525 'rangePickerHeaderHeadlineStyle: TextStyle(inherit: true, size: 14.0)',
526 'rangePickerHeaderHelpStyle: TextStyle(inherit: true, size: 15.0)',
527 'rangeSelectionBackgroundColor: ${const Color(0xffffff2f)}',
528 'rangeSelectionOverlayColor: WidgetStatePropertyAll(${const Color(0xffffff3f)})',
529 'dividerColor: ${const Color(0xffffff4f)}',
530 'inputDecorationTheme: InputDecorationThemeData#00000(fillColor: ${const Color(0xffffff5f)}, border: UnderlineInputBorder())',
531 'cancelButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(${const Color(0xffffff6f)}))',
532 'confirmButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(${const Color(0xffffff7f)}))',
533 'locale: en',
534 'toggleButtonTextStyle: TextStyle(inherit: true, size: 13.0)',
535 'subHeaderForegroundColor: ${const Color(0xffffff8f)}',
536 ]),
537 );
538 });
539
540 testWidgets('DatePickerDialog uses ThemeData datePicker theme (calendar mode)', (
541 WidgetTester tester,
542 ) async {
543 await tester.pumpWidget(
544 MaterialApp(
545 theme: ThemeData(datePickerTheme: datePickerTheme),
546 home: Directionality(
547 textDirection: TextDirection.ltr,
548 child: Material(
549 child: Center(
550 child: DatePickerDialog(
551 initialDate: DateTime(2023, DateTime.january, 25),
552 firstDate: DateTime(2022),
553 lastDate: DateTime(2024, DateTime.december, 31),
554 currentDate: DateTime(2023, DateTime.january, 24),
555 ),
556 ),
557 ),
558 ),
559 ),
560 );
561
562 final Material material = findDialogMaterial(tester);
563 expect(material.color, datePickerTheme.backgroundColor);
564 expect(material.elevation, datePickerTheme.elevation);
565 expect(material.shadowColor, datePickerTheme.shadowColor);
566 expect(material.surfaceTintColor, datePickerTheme.surfaceTintColor);
567 expect(material.shape, datePickerTheme.shape);
568
569 final Text selectDate = tester.widget<Text>(find.text('Select date'));
570 final Material headerMaterial = findHeaderMaterial(tester, 'Select date');
571 expect(selectDate.style?.color, datePickerTheme.headerForegroundColor);
572 expect(selectDate.style?.fontSize, datePickerTheme.headerHelpStyle?.fontSize);
573 expect(headerMaterial.color, datePickerTheme.headerBackgroundColor);
574
575 final Text weekday = tester.widget<Text>(find.text('W'));
576 expect(weekday.style?.color, datePickerTheme.weekdayStyle?.color);
577 expect(weekday.style?.fontSize, datePickerTheme.weekdayStyle?.fontSize);
578
579 final Text selectedDate = tester.widget<Text>(find.text('Wed, Jan 25'));
580 expect(selectedDate.style?.color, datePickerTheme.headerForegroundColor);
581 expect(selectedDate.style?.fontSize, datePickerTheme.headerHeadlineStyle?.fontSize);
582
583 final Text day31 = tester.widget<Text>(find.text('31'));
584 final ShapeDecoration day31Decoration = findDayDecoration(tester, '31')!;
585 expect(day31.style?.color, datePickerTheme.dayForegroundColor?.resolve(<MaterialState>{}));
586 expect(day31.style?.fontSize, datePickerTheme.dayStyle?.fontSize);
587 expect(day31Decoration.color, datePickerTheme.dayBackgroundColor?.resolve(<MaterialState>{}));
588 expect(day31Decoration.shape, datePickerTheme.dayShape?.resolve(<MaterialState>{}));
589
590 final Text day24 = tester.widget<Text>(find.text('24')); // DatePickerDialog.currentDate
591 final ShapeDecoration day24Decoration = findDayDecoration(tester, '24')!;
592 final OutlinedBorder day24Shape = day24Decoration.shape as OutlinedBorder;
593 expect(day24.style?.fontSize, datePickerTheme.dayStyle?.fontSize);
594 expect(day24.style?.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}));
595 expect(day24Decoration.color, datePickerTheme.todayBackgroundColor?.resolve(<MaterialState>{}));
596 expect(
597 day24Decoration.shape,
598 datePickerTheme.dayShape
599 ?.resolve(<MaterialState>{})!
600 .copyWith(
601 side: datePickerTheme.todayBorder?.copyWith(
602 color: datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}),
603 ),
604 ),
605 );
606 expect(day24Shape.side.width, datePickerTheme.todayBorder?.width);
607
608 // Test the toggle mode button style.
609 final Text january2023 = tester.widget<Text>(find.text('January 2023'));
610 expect(january2023.style?.fontSize, datePickerTheme.toggleButtonTextStyle?.fontSize);
611 expect(january2023.style?.color, datePickerTheme.subHeaderForegroundColor);
612 final Icon arrowIcon = tester.widget<Icon>(find.byIcon(Icons.arrow_drop_down));
613 expect(arrowIcon.color, datePickerTheme.subHeaderForegroundColor);
614
615 // Test the day overlay color.
616 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere(
617 (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures',
618 );
619 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
620 await gesture.addPointer();
621 await gesture.moveTo(tester.getCenter(find.text('25')));
622 await tester.pumpAndSettle();
623 expect(
624 inkFeatures,
625 paints..circle(color: datePickerTheme.dayOverlayColor?.resolve(<MaterialState>{})),
626 );
627
628 // Show the year selector.
629
630 await tester.tap(find.text('January 2023'));
631 await tester.pumpAndSettle();
632
633 final Text year2022 = tester.widget<Text>(find.text('2022'));
634 final ShapeDecoration year2022Decoration = findTextDecoration(tester, '2022')!;
635 expect(year2022.style?.fontSize, datePickerTheme.yearStyle?.fontSize);
636 expect(year2022.style?.color, datePickerTheme.yearForegroundColor?.resolve(<MaterialState>{}));
637 expect(
638 year2022Decoration.color,
639 datePickerTheme.yearBackgroundColor?.resolve(<MaterialState>{}),
640 );
641 expect(year2022Decoration.shape, datePickerTheme.yearShape?.resolve(<MaterialState>{}));
642
643 final Text year2023 = tester.widget<Text>(find.text('2023')); // DatePickerDialog.currentDate
644 final ShapeDecoration year2023Decoration = findTextDecoration(tester, '2023')!;
645 expect(year2023.style?.fontSize, datePickerTheme.yearStyle?.fontSize);
646 expect(year2023.style?.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}));
647 expect(
648 year2023Decoration.color,
649 datePickerTheme.todayBackgroundColor?.resolve(<MaterialState>{}),
650 );
651 final RoundedRectangleBorder roundedRectangleBorder =
652 year2023Decoration.shape as RoundedRectangleBorder;
653 expect(roundedRectangleBorder.side.width, datePickerTheme.todayBorder?.width);
654 expect(
655 roundedRectangleBorder.side.color,
656 datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}),
657 );
658
659 // Test the year overlay color.
660 await gesture.moveTo(tester.getCenter(find.text('2024')));
661 await tester.pumpAndSettle();
662 expect(
663 inkFeatures,
664 paints..rect(color: datePickerTheme.yearOverlayColor?.resolve(<MaterialState>{})),
665 );
666
667 final ButtonStyle cancelButtonStyle = actionButtonStyle(tester, 'Cancel');
668 expect(
669 cancelButtonStyle.toString(),
670 equalsIgnoringHashCodes(datePickerTheme.cancelButtonStyle.toString()),
671 );
672
673 final ButtonStyle confirmButtonStyle = actionButtonStyle(tester, 'OK');
674 expect(
675 confirmButtonStyle.toString(),
676 equalsIgnoringHashCodes(datePickerTheme.confirmButtonStyle.toString()),
677 );
678 });
679
680 testWidgets('DatePickerDialog uses ThemeData datePicker theme (input mode)', (
681 WidgetTester tester,
682 ) async {
683 await tester.pumpWidget(
684 MaterialApp(
685 theme: ThemeData(datePickerTheme: datePickerTheme),
686 home: Directionality(
687 textDirection: TextDirection.ltr,
688 child: Material(
689 child: Center(
690 child: DatePickerDialog(
691 initialEntryMode: DatePickerEntryMode.input,
692 initialDate: DateTime(2023, DateTime.january, 25),
693 firstDate: DateTime(2022),
694 lastDate: DateTime(2024, DateTime.december, 31),
695 currentDate: DateTime(2023, DateTime.january, 24),
696 ),
697 ),
698 ),
699 ),
700 ),
701 );
702
703 final Material material = findDialogMaterial(tester);
704 expect(material.color, datePickerTheme.backgroundColor);
705 expect(material.elevation, datePickerTheme.elevation);
706 expect(material.shadowColor, datePickerTheme.shadowColor);
707 expect(material.surfaceTintColor, datePickerTheme.surfaceTintColor);
708 expect(material.shape, datePickerTheme.shape);
709
710 final Text selectDate = tester.widget<Text>(find.text('Select date'));
711 final Material headerMaterial = findHeaderMaterial(tester, 'Select date');
712 expect(selectDate.style?.color, datePickerTheme.headerForegroundColor);
713 expect(selectDate.style?.fontSize, datePickerTheme.headerHelpStyle?.fontSize);
714 expect(headerMaterial.color, datePickerTheme.headerBackgroundColor);
715
716 final InputDecoration inputDecoration = tester
717 .widget<TextField>(find.byType(TextField))
718 .decoration!;
719 expect(inputDecoration.fillColor, datePickerTheme.inputDecorationTheme?.fillColor);
720
721 final ButtonStyle cancelButtonStyle = actionButtonStyle(tester, 'Cancel');
722 expect(
723 cancelButtonStyle.toString(),
724 equalsIgnoringHashCodes(datePickerTheme.cancelButtonStyle.toString()),
725 );
726
727 final ButtonStyle confirmButtonStyle = actionButtonStyle(tester, 'OK');
728 expect(
729 confirmButtonStyle.toString(),
730 equalsIgnoringHashCodes(datePickerTheme.confirmButtonStyle.toString()),
731 );
732 });
733
734 testWidgets('DateRangePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async {
735 await tester.pumpWidget(
736 MaterialApp(
737 theme: ThemeData(datePickerTheme: datePickerTheme),
738 home: Directionality(
739 textDirection: TextDirection.ltr,
740 child: Material(
741 child: Center(
742 child: DateRangePickerDialog(
743 firstDate: DateTime(2023),
744 lastDate: DateTime(2023, DateTime.january, 31),
745 initialDateRange: DateTimeRange(
746 start: DateTime(2023, DateTime.january, 17),
747 end: DateTime(2023, DateTime.january, 20),
748 ),
749 currentDate: DateTime(2023, DateTime.january, 23),
750 ),
751 ),
752 ),
753 ),
754 ),
755 );
756
757 final Material material = findDialogMaterial(tester);
758 expect(material.color, datePickerTheme.backgroundColor);
759 expect(
760 tester.widget<Scaffold>(find.byType(Scaffold)).backgroundColor,
761 datePickerTheme.rangePickerBackgroundColor,
762 );
763 expect(material.elevation, datePickerTheme.rangePickerElevation);
764 expect(material.shadowColor, datePickerTheme.rangePickerShadowColor);
765 expect(material.surfaceTintColor, datePickerTheme.rangePickerSurfaceTintColor);
766 expect(material.shape, datePickerTheme.rangePickerShape);
767
768 final AppBar appBar = tester.widget<AppBar>(find.byType(AppBar));
769 expect(appBar.backgroundColor, datePickerTheme.rangePickerHeaderBackgroundColor);
770
771 final Text selectRange = tester.widget<Text>(find.text('Select range'));
772 expect(selectRange.style?.color, datePickerTheme.rangePickerHeaderForegroundColor);
773 expect(selectRange.style?.fontSize, datePickerTheme.rangePickerHeaderHelpStyle?.fontSize);
774
775 final Text selectedDate = tester.widget<Text>(find.text('Jan 17'));
776 expect(selectedDate.style?.color, datePickerTheme.rangePickerHeaderForegroundColor);
777 expect(selectedDate.style?.fontSize, datePickerTheme.rangePickerHeaderHeadlineStyle?.fontSize);
778
779 // Test the day overlay color.
780 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere(
781 (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures',
782 );
783 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
784 await gesture.addPointer();
785 await gesture.moveTo(tester.getCenter(find.text('16')));
786 await tester.pumpAndSettle();
787 expect(
788 inkFeatures,
789 paints..circle(color: datePickerTheme.dayOverlayColor?.resolve(<MaterialState>{})),
790 );
791
792 // Test the range selection overlay color.
793 await gesture.moveTo(tester.getCenter(find.text('18')));
794 await tester.pumpAndSettle();
795 expect(
796 inkFeatures,
797 paints..circle(color: datePickerTheme.rangeSelectionOverlayColor?.resolve(<MaterialState>{})),
798 );
799 });
800
801 testWidgets('Material2 - DateRangePickerDialog uses ThemeData datePicker theme', (
802 WidgetTester tester,
803 ) async {
804 await tester.pumpWidget(
805 MaterialApp(
806 theme: ThemeData(datePickerTheme: datePickerTheme, useMaterial3: false),
807 home: Directionality(
808 textDirection: TextDirection.ltr,
809 child: Material(
810 child: Center(
811 child: DateRangePickerDialog(
812 firstDate: DateTime(2023),
813 lastDate: DateTime(2023, DateTime.january, 31),
814 initialDateRange: DateTimeRange(
815 start: DateTime(2023, DateTime.january, 17),
816 end: DateTime(2023, DateTime.january, 20),
817 ),
818 currentDate: DateTime(2023, DateTime.january, 23),
819 ),
820 ),
821 ),
822 ),
823 ),
824 );
825
826 final Material material = findDialogMaterial(tester);
827 expect(material.color, datePickerTheme.backgroundColor);
828 expect(
829 tester.widget<Scaffold>(find.byType(Scaffold)).backgroundColor,
830 datePickerTheme.rangePickerBackgroundColor,
831 );
832 expect(material.elevation, datePickerTheme.rangePickerElevation);
833 expect(material.shadowColor, datePickerTheme.rangePickerShadowColor);
834 expect(material.surfaceTintColor, datePickerTheme.rangePickerSurfaceTintColor);
835 expect(material.shape, datePickerTheme.rangePickerShape);
836
837 final AppBar appBar = tester.widget<AppBar>(find.byType(AppBar));
838 expect(appBar.backgroundColor, datePickerTheme.rangePickerHeaderBackgroundColor);
839
840 final Text selectRange = tester.widget<Text>(find.text('SELECT RANGE'));
841 expect(selectRange.style?.color, datePickerTheme.rangePickerHeaderForegroundColor);
842 expect(selectRange.style?.fontSize, datePickerTheme.rangePickerHeaderHelpStyle?.fontSize);
843
844 final Text selectedDate = tester.widget<Text>(find.text('Jan 17'));
845 expect(selectedDate.style?.color, datePickerTheme.rangePickerHeaderForegroundColor);
846 expect(selectedDate.style?.fontSize, datePickerTheme.rangePickerHeaderHeadlineStyle?.fontSize);
847
848 // Test the day overlay color.
849 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere(
850 (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures',
851 );
852 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
853 await gesture.addPointer();
854 await gesture.moveTo(tester.getCenter(find.text('16')));
855 await tester.pumpAndSettle();
856 expect(
857 inkFeatures,
858 paints..circle(color: datePickerTheme.dayOverlayColor?.resolve(<MaterialState>{})),
859 );
860
861 // Test the range selection overlay color.
862 await gesture.moveTo(tester.getCenter(find.text('18')));
863 await tester.pumpAndSettle();
864 expect(
865 inkFeatures,
866 paints..circle(color: datePickerTheme.rangeSelectionOverlayColor?.resolve(<MaterialState>{})),
867 );
868 });
869
870 testWidgets('Dividers use DatePickerThemeData.dividerColor', (WidgetTester tester) async {
871 Future<void> showPicker(WidgetTester tester, Size size) async {
872 tester.view.physicalSize = size;
873 tester.view.devicePixelRatio = 1.0;
874 addTearDown(tester.view.reset);
875 await tester.pumpWidget(
876 MaterialApp(
877 theme: ThemeData(datePickerTheme: datePickerTheme),
878 home: Directionality(
879 textDirection: TextDirection.ltr,
880 child: Material(
881 child: Center(
882 child: DatePickerDialog(
883 initialDate: DateTime(2023, DateTime.january, 25),
884 firstDate: DateTime(2022),
885 lastDate: DateTime(2024, DateTime.december, 31),
886 currentDate: DateTime(2023, DateTime.january, 24),
887 ),
888 ),
889 ),
890 ),
891 ),
892 );
893 }
894
895 await showPicker(tester, wideWindowSize);
896
897 // Test vertical divider.
898 final VerticalDivider verticalDivider = tester.widget(find.byType(VerticalDivider));
899 expect(verticalDivider.color, datePickerTheme.dividerColor);
900
901 // Test portrait layout.
902 await showPicker(tester, narrowWindowSize);
903
904 // Test horizontal divider.
905 final Divider horizontalDivider = tester.widget(find.byType(Divider));
906 expect(horizontalDivider.color, datePickerTheme.dividerColor);
907 });
908
909 testWidgets('DatePicker uses ThemeData.inputDecorationTheme properties '
910 'which are null in DatePickerThemeData.inputDecorationTheme', (WidgetTester tester) async {
911 Widget buildWidget({
912 InputDecorationThemeData? inputDecorationTheme,
913 DatePickerThemeData? datePickerTheme,
914 }) {
915 return MaterialApp(
916 theme: ThemeData(
917 inputDecorationTheme: inputDecorationTheme,
918 datePickerTheme: datePickerTheme,
919 ),
920 home: Directionality(
921 textDirection: TextDirection.ltr,
922 child: Material(
923 child: Center(
924 child: DatePickerDialog(
925 initialEntryMode: DatePickerEntryMode.input,
926 initialDate: DateTime(2023, DateTime.january, 25),
927 firstDate: DateTime(2022),
928 lastDate: DateTime(2024, DateTime.december, 31),
929 currentDate: DateTime(2023, DateTime.january, 24),
930 ),
931 ),
932 ),
933 ),
934 );
935 }
936
937 // Test DatePicker with DatePickerThemeData.inputDecorationTheme.
938 await tester.pumpWidget(
939 buildWidget(
940 inputDecorationTheme: const InputDecorationThemeData(filled: true),
941 datePickerTheme: datePickerTheme,
942 ),
943 );
944 InputDecoration inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
945 expect(inputDecoration.fillColor, datePickerTheme.inputDecorationTheme!.fillColor);
946 expect(inputDecoration.border, datePickerTheme.inputDecorationTheme!.border);
947
948 // Test DatePicker with ThemeData.inputDecorationTheme.
949 await tester.pumpWidget(
950 buildWidget(
951 inputDecorationTheme: const InputDecorationThemeData(
952 filled: true,
953 fillColor: Color(0xFF00FF00),
954 border: OutlineInputBorder(),
955 ),
956 ),
957 );
958 await tester.pumpAndSettle();
959
960 inputDecoration = tester.widget<TextField>(find.byType(TextField)).decoration!;
961 expect(inputDecoration.fillColor, const Color(0xFF00FF00));
962 expect(inputDecoration.border, const OutlineInputBorder());
963 });
964
965 testWidgets('DatePickerDialog resolves DatePickerTheme.dayOverlayColor states', (
966 WidgetTester tester,
967 ) async {
968 final MaterialStateProperty<Color> dayOverlayColor = MaterialStateProperty.resolveWith<Color>((
969 Set<MaterialState> states,
970 ) {
971 if (states.contains(MaterialState.hovered)) {
972 return const Color(0xff00ff00);
973 }
974 if (states.contains(MaterialState.focused)) {
975 return const Color(0xffff00ff);
976 }
977 if (states.contains(MaterialState.pressed)) {
978 return const Color(0xffffff00);
979 }
980 return Colors.transparent;
981 });
982
983 await tester.pumpWidget(
984 MaterialApp(
985 theme: ThemeData(datePickerTheme: DatePickerThemeData(dayOverlayColor: dayOverlayColor)),
986 home: Directionality(
987 textDirection: TextDirection.ltr,
988 child: Material(
989 child: Center(
990 child: Focus(
991 child: DatePickerDialog(
992 initialDate: DateTime(2023, DateTime.january, 25),
993 firstDate: DateTime(2022),
994 lastDate: DateTime(2024, DateTime.december, 31),
995 currentDate: DateTime(2023, DateTime.january, 24),
996 ),
997 ),
998 ),
999 ),
1000 ),
1001 ),
1002 );
1003
1004 MaterialInkController findDayGridMaterial(WidgetTester tester) {
1005 // All days are painted on the same Material widget.
1006 // Use an arbitrary day to find this Material.
1007 return Material.of(tester.element(find.text('17')));
1008 }
1009
1010 // Test the hover overlay color.
1011 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
1012 await gesture.addPointer();
1013 await gesture.moveTo(tester.getCenter(find.text('20')));
1014 await tester.pumpAndSettle();
1015
1016 expect(
1017 findDayGridMaterial(tester),
1018 paints
1019 ..circle() // Today decoration.
1020 ..circle() // Selected day decoration.
1021 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered})),
1022 );
1023
1024 // Test the pressed overlay color.
1025 await gesture.down(tester.getCenter(find.text('20')));
1026 await tester.pumpAndSettle();
1027 if (kIsWeb) {
1028 // An extra circle is painted on the web for the hovered state.
1029 expect(
1030 findDayGridMaterial(tester),
1031 paints
1032 ..circle() // Today decoration.
1033 ..circle() // Selected day decoration.
1034 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
1035 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
1036 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.pressed})),
1037 );
1038 } else {
1039 expect(
1040 findDayGridMaterial(tester),
1041 paints
1042 ..circle() // Today decoration.
1043 ..circle() // Selected day decoration.
1044 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
1045 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.pressed})),
1046 );
1047 }
1048
1049 await gesture.removePointer();
1050 await tester.pumpAndSettle();
1051
1052 // Focus day selection.
1053 for (int i = 0; i < 5; i++) {
1054 await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1055 await tester.pumpAndSettle();
1056 }
1057
1058 // Test the focused overlay color.
1059 expect(
1060 findDayGridMaterial(tester),
1061 paints
1062 ..circle() // Today decoration.
1063 ..circle() // Selected day decoration.
1064 ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.focused})),
1065 );
1066 });
1067
1068 testWidgets('DatePickerDialog resolves DatePickerTheme.yearOverlayColor states', (
1069 WidgetTester tester,
1070 ) async {
1071 final MaterialStateProperty<Color> yearOverlayColor = MaterialStateProperty.resolveWith<Color>((
1072 Set<MaterialState> states,
1073 ) {
1074 if (states.contains(MaterialState.hovered)) {
1075 return const Color(0xff00ff00);
1076 }
1077 if (states.contains(MaterialState.focused)) {
1078 return const Color(0xffff00ff);
1079 }
1080 if (states.contains(MaterialState.pressed)) {
1081 return const Color(0xffffff00);
1082 }
1083 return Colors.transparent;
1084 });
1085
1086 await tester.pumpWidget(
1087 MaterialApp(
1088 theme: ThemeData(datePickerTheme: DatePickerThemeData(yearOverlayColor: yearOverlayColor)),
1089 home: Directionality(
1090 textDirection: TextDirection.ltr,
1091 child: Material(
1092 child: Center(
1093 child: Focus(
1094 child: DatePickerDialog(
1095 initialDate: DateTime(2023, DateTime.january, 25),
1096 firstDate: DateTime(2022),
1097 lastDate: DateTime(2024, DateTime.december, 31),
1098 currentDate: DateTime(2023, DateTime.january, 24),
1099 initialCalendarMode: DatePickerMode.year,
1100 ),
1101 ),
1102 ),
1103 ),
1104 ),
1105 ),
1106 );
1107
1108 // Test the hover overlay color.
1109 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere(
1110 (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures',
1111 );
1112 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
1113 await gesture.addPointer();
1114 await gesture.moveTo(tester.getCenter(find.text('2022')));
1115 await tester.pumpAndSettle();
1116 expect(
1117 inkFeatures,
1118 paints..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.hovered})),
1119 );
1120
1121 // Test the pressed overlay color.
1122 await gesture.down(tester.getCenter(find.text('2022')));
1123 await tester.pumpAndSettle();
1124 expect(
1125 inkFeatures,
1126 paints
1127 ..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.hovered}))
1128 ..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.pressed})),
1129 );
1130
1131 await gesture.removePointer();
1132 await tester.pumpAndSettle();
1133
1134 // Focus year selection.
1135 for (int i = 0; i < 3; i++) {
1136 await tester.sendKeyEvent(LogicalKeyboardKey.tab);
1137 await tester.pumpAndSettle();
1138 }
1139
1140 // Test the focused overlay color.
1141 expect(
1142 inkFeatures,
1143 paints..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.focused})),
1144 );
1145 });
1146
1147 testWidgets('DateRangePickerDialog resolves DatePickerTheme.rangeSelectionOverlayColor states', (
1148 WidgetTester tester,
1149 ) async {
1150 final MaterialStateProperty<Color> rangeSelectionOverlayColor =
1151 MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
1152 if (states.contains(MaterialState.hovered)) {
1153 return const Color(0xff00ff00);
1154 }
1155 if (states.contains(MaterialState.pressed)) {
1156 return const Color(0xffffff00);
1157 }
1158 return Colors.transparent;
1159 });
1160
1161 await tester.pumpWidget(
1162 MaterialApp(
1163 theme: ThemeData(
1164 datePickerTheme: DatePickerThemeData(
1165 rangeSelectionOverlayColor: rangeSelectionOverlayColor,
1166 ),
1167 ),
1168 home: Directionality(
1169 textDirection: TextDirection.ltr,
1170 child: Material(
1171 child: Center(
1172 child: DateRangePickerDialog(
1173 firstDate: DateTime(2023),
1174 lastDate: DateTime(2023, DateTime.january, 31),
1175 initialDateRange: DateTimeRange(
1176 start: DateTime(2023, DateTime.january, 17),
1177 end: DateTime(2023, DateTime.january, 20),
1178 ),
1179 currentDate: DateTime(2023, DateTime.january, 23),
1180 ),
1181 ),
1182 ),
1183 ),
1184 ),
1185 );
1186
1187 // Test the hover overlay color.
1188 final RenderObject inkFeatures = tester.allRenderObjects.firstWhere(
1189 (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures',
1190 );
1191 final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
1192 await gesture.addPointer();
1193 await gesture.moveTo(tester.getCenter(find.text('18')));
1194 await tester.pumpAndSettle();
1195 expect(
1196 inkFeatures,
1197 paints
1198 ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered})),
1199 );
1200
1201 // Test the pressed overlay color.
1202 await gesture.down(tester.getCenter(find.text('18')));
1203 await tester.pumpAndSettle();
1204 if (kIsWeb) {
1205 // An extra circle is painted on the web for the hovered state.
1206 expect(
1207 inkFeatures,
1208 paints
1209 ..circle(
1210 color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered}),
1211 )
1212 ..circle(
1213 color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered}),
1214 )
1215 ..circle(
1216 color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.pressed}),
1217 ),
1218 );
1219 } else {
1220 expect(
1221 inkFeatures,
1222 paints
1223 ..circle(
1224 color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered}),
1225 )
1226 ..circle(
1227 color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.pressed}),
1228 ),
1229 );
1230 }
1231 });
1232
1233 testWidgets('YearPicker maintains default year shape at textScaleFactor 1, 1.5, 2', (
1234 WidgetTester tester,
1235 ) async {
1236 double textScaleFactor = 1.0;
1237 Widget buildFrame() {
1238 return MaterialApp(
1239 home: Builder(
1240 builder: (BuildContext context) {
1241 return MediaQuery.withClampedTextScaling(
1242 minScaleFactor: textScaleFactor,
1243 maxScaleFactor: textScaleFactor,
1244 child: Scaffold(
1245 body: YearPicker(
1246 currentDate: DateTime(2025),
1247 firstDate: DateTime(2021),
1248 lastDate: DateTime(2030),
1249 selectedDate: DateTime(2025),
1250 onChanged: (DateTime value) {},
1251 ),
1252 ),
1253 );
1254 },
1255 ),
1256 );
1257 }
1258
1259 await tester.pumpWidget(buildFrame());
1260
1261 // Find container whose child is text 2025.
1262 final Finder yearContainer = find
1263 .ancestor(of: find.text('2025'), matching: find.byType(Container))
1264 .first;
1265
1266 expect(
1267 tester.renderObject(yearContainer),
1268 paints..rrect(
1269 rrect: RRect.fromLTRBR(0.5, 0.5, 71.5, 35.5, const Radius.circular(17.5)),
1270 color: const Color(0xFF6750A4),
1271 ),
1272 );
1273
1274 textScaleFactor = 1.5;
1275 await tester.pumpWidget(buildFrame());
1276
1277 expect(
1278 tester.renderObject(yearContainer),
1279 paints..rrect(
1280 rrect: RRect.fromLTRBR(0.5, 0.5, 107.5, 51.5, const Radius.circular(25.5)),
1281 color: const Color(0xFF6750A4),
1282 ),
1283 );
1284
1285 textScaleFactor = 2;
1286 await tester.pumpWidget(buildFrame());
1287
1288 expect(
1289 tester.renderObject(yearContainer),
1290 paints..rrect(
1291 rrect: RRect.fromLTRBR(0.5, 0.5, 143.5, 51.5, const Radius.circular(25.5)),
1292 color: const Color(0xFF6750A4),
1293 ),
1294 );
1295 });
1296
1297 testWidgets('YearPicker applies shape from DatePickerThemeData.yearShape correctly', (
1298 WidgetTester tester,
1299 ) async {
1300 const OutlinedBorder yearShpae = CircleBorder();
1301 await tester.pumpWidget(
1302 MaterialApp(
1303 theme: ThemeData(
1304 datePickerTheme: datePickerTheme.copyWith(
1305 yearShape: MaterialStateProperty.all<OutlinedBorder>(yearShpae),
1306 ),
1307 ),
1308 home: Directionality(
1309 textDirection: TextDirection.ltr,
1310 child: Material(
1311 child: Center(
1312 child: YearPicker(
1313 currentDate: DateTime(2025),
1314 firstDate: DateTime(2021),
1315 lastDate: DateTime(2030),
1316 selectedDate: DateTime(2025),
1317 onChanged: (DateTime value) {},
1318 ),
1319 ),
1320 ),
1321 ),
1322 ),
1323 );
1324
1325 final ShapeDecoration year2022Decoration = findTextDecoration(tester, '2022')!;
1326 final OutlinedBorder year2022roundedRectangleBorder = year2022Decoration.shape as CircleBorder;
1327 expect(year2022roundedRectangleBorder.side.width, 0.0);
1328 expect(year2022roundedRectangleBorder.side.color, yearShpae.side.color);
1329
1330 final ShapeDecoration year2025Decoration = findTextDecoration(tester, '2025')!;
1331 final OutlinedBorder year2022RoundedRectangleBorder = year2025Decoration.shape as CircleBorder;
1332 expect(year2022RoundedRectangleBorder.side.width, datePickerTheme.todayBorder?.width);
1333 expect(
1334 year2022RoundedRectangleBorder.side.color,
1335 datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{}),
1336 );
1337 });
1338
1339 testWidgets('Toggle button uses DatePickerTheme.toggleButtonTextStyle.color when it is defined', (
1340 WidgetTester tester,
1341 ) async {
1342 const Color toggleButtonTextColor = Color(0xff00ff00);
1343 const Color subHeaderForegroundColor = Color(0xffff0000);
1344
1345 await tester.pumpWidget(
1346 MaterialApp(
1347 theme: ThemeData(
1348 datePickerTheme: const DatePickerThemeData(
1349 toggleButtonTextStyle: TextStyle(color: toggleButtonTextColor),
1350 subHeaderForegroundColor: subHeaderForegroundColor,
1351 ),
1352 ),
1353 home: DatePickerDialog(
1354 initialDate: DateTime(2023, DateTime.january, 25),
1355 firstDate: DateTime(2022),
1356 lastDate: DateTime(2024, DateTime.december, 31),
1357 currentDate: DateTime(2023, DateTime.january, 24),
1358 ),
1359 ),
1360 );
1361
1362 final Text toggleButtonText = tester.widget(find.text('January 2023'));
1363 expect(toggleButtonText.style?.color, toggleButtonTextColor);
1364 });
1365
1366 testWidgets(
1367 'Toggle button uses DatePickerTheme.subHeaderForegroundColor when DatePickerTheme.toggleButtonTextStyle.color is not defined',
1368 (WidgetTester tester) async {
1369 const Color subHeaderForegroundColor = Color(0xffff0000);
1370
1371 await tester.pumpWidget(
1372 MaterialApp(
1373 theme: ThemeData(
1374 datePickerTheme: const DatePickerThemeData(
1375 toggleButtonTextStyle: TextStyle(),
1376 subHeaderForegroundColor: subHeaderForegroundColor,
1377 ),
1378 ),
1379 home: DatePickerDialog(
1380 initialDate: DateTime(2023, DateTime.january, 25),
1381 firstDate: DateTime(2022),
1382 lastDate: DateTime(2024, DateTime.december, 31),
1383 currentDate: DateTime(2023, DateTime.january, 24),
1384 ),
1385 ),
1386 );
1387
1388 final Text toggleButtonText = tester.widget(find.text('January 2023'));
1389 expect(toggleButtonText.style?.color, subHeaderForegroundColor);
1390 },
1391 );
1392}
1393