// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { const DatePickerThemeData datePickerTheme = DatePickerThemeData( backgroundColor: Color(0xfffffff0), elevation: 6, shadowColor: Color(0xfffffff1), surfaceTintColor: Color(0xfffffff2), shape: RoundedRectangleBorder(), headerBackgroundColor: Color(0xfffffff3), headerForegroundColor: Color(0xfffffff4), headerHeadlineStyle: TextStyle(fontSize: 10), headerHelpStyle: TextStyle(fontSize: 11), weekdayStyle: TextStyle(fontSize: 12), dayStyle: TextStyle(fontSize: 13), dayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff5)), dayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff6)), dayOverlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff7)), todayForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff8)), todayBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff9)), todayBorder: BorderSide(width: 3), yearStyle: TextStyle(fontSize: 13), yearForegroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffffa)), yearBackgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffffb)), yearOverlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffffc)), rangePickerBackgroundColor: Color(0xfffffffd), rangePickerElevation: 7, rangePickerShadowColor: Color(0xfffffffe), rangePickerSurfaceTintColor: Color(0xffffffff), rangePickerShape: RoundedRectangleBorder(), rangePickerHeaderBackgroundColor: Color(0xffffff0f), rangePickerHeaderForegroundColor: Color(0xffffff1f), rangePickerHeaderHeadlineStyle: TextStyle(fontSize: 14), rangePickerHeaderHelpStyle: TextStyle(fontSize: 15), rangeSelectionBackgroundColor: Color(0xffffff2f), rangeSelectionOverlayColor: MaterialStatePropertyAll<Color>(Color(0xffffff3f)), dividerColor: Color(0xffffff4f), inputDecorationTheme: InputDecorationTheme( fillColor: Color(0xffffff5f), border: UnderlineInputBorder(), ), cancelButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff6f))), confirmButtonStyle: ButtonStyle(foregroundColor: MaterialStatePropertyAll<Color>(Color(0xffffff7f))), ); Material findDialogMaterial(WidgetTester tester) { return tester.widget<Material>( find.descendant( of: find.byType(Dialog), matching: find.byType(Material) ).first ); } Material findHeaderMaterial(WidgetTester tester, String text) { return tester.widget<Material>( find.ancestor( of: find.text(text), matching: find.byType(Material) ).first, ); } BoxDecoration? findTextDecoration(WidgetTester tester, String date) { final Container container = tester.widget<Container>( find.ancestor( of: find.text(date), matching: find.byType(Container) ).first, ); return container.decoration as BoxDecoration?; } ButtonStyle actionButtonStyle(WidgetTester tester, String text) { return tester.widget<TextButton>(find.widgetWithText(TextButton, text)).style!; } const Size wideWindowSize = Size(1920.0, 1080.0); const Size narrowWindowSize = Size(1070.0, 1770.0); test('DatePickerThemeData copyWith, ==, hashCode basics', () { expect(const DatePickerThemeData(), const DatePickerThemeData().copyWith()); expect(const DatePickerThemeData().hashCode, const DatePickerThemeData().copyWith().hashCode); }); test('DatePickerThemeData lerp special cases', () { const DatePickerThemeData data = DatePickerThemeData(); expect(identical(DatePickerThemeData.lerp(data, data, 0.5), data), true); }); test('DatePickerThemeData defaults', () { const DatePickerThemeData theme = DatePickerThemeData(); expect(theme.backgroundColor, null); expect(theme.elevation, null); expect(theme.shadowColor, null); expect(theme.surfaceTintColor, null); expect(theme.shape, null); expect(theme.headerBackgroundColor, null); expect(theme.headerForegroundColor, null); expect(theme.headerHeadlineStyle, null); expect(theme.headerHelpStyle, null); expect(theme.weekdayStyle, null); expect(theme.dayStyle, null); expect(theme.dayForegroundColor, null); expect(theme.dayBackgroundColor, null); expect(theme.dayOverlayColor, null); expect(theme.todayForegroundColor, null); expect(theme.todayBackgroundColor, null); expect(theme.todayBorder, null); expect(theme.yearStyle, null); expect(theme.yearForegroundColor, null); expect(theme.yearBackgroundColor, null); expect(theme.yearOverlayColor, null); expect(theme.rangePickerBackgroundColor, null); expect(theme.rangePickerElevation, null); expect(theme.rangePickerShadowColor, null); expect(theme.rangePickerSurfaceTintColor, null); expect(theme.rangePickerShape, null); expect(theme.rangePickerHeaderBackgroundColor, null); expect(theme.rangePickerHeaderForegroundColor, null); expect(theme.rangePickerHeaderHeadlineStyle, null); expect(theme.rangePickerHeaderHelpStyle, null); expect(theme.rangeSelectionBackgroundColor, null); expect(theme.rangeSelectionOverlayColor, null); expect(theme.dividerColor, null); expect(theme.inputDecorationTheme, null); expect(theme.cancelButtonStyle, null); expect(theme.confirmButtonStyle, null); }); testWidgetsWithLeakTracking('DatePickerTheme.defaults M3 defaults', (WidgetTester tester) async { late final DatePickerThemeData m3; // M3 Defaults late final ThemeData theme; late final ColorScheme colorScheme; late final TextTheme textTheme; await tester.pumpWidget( MaterialApp( theme: ThemeData(useMaterial3: true), home: Builder( builder: (BuildContext context) { m3 = DatePickerTheme.defaults(context); theme = Theme.of(context); colorScheme = theme.colorScheme; textTheme = theme.textTheme; return Container(); }, ), ), ); expect(m3.backgroundColor, colorScheme.surface); expect(m3.elevation, 6); expect(m3.shadowColor, const Color(0x00000000)); // Colors.transparent expect(m3.surfaceTintColor, colorScheme.surfaceTint); expect(m3.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(28))); expect(m3.headerBackgroundColor, const Color(0x00000000)); // Colors.transparent expect(m3.headerForegroundColor, colorScheme.onSurfaceVariant); expect(m3.headerHeadlineStyle, textTheme.headlineLarge); expect(m3.headerHelpStyle, textTheme.labelLarge); expect(m3.weekdayStyle, textTheme.bodyLarge?.apply(color: colorScheme.onSurface)); expect(m3.dayStyle, textTheme.bodyLarge); expect(m3.dayForegroundColor?.resolve(<MaterialState>{}), colorScheme.onSurface); expect(m3.dayForegroundColor?.resolve(<MaterialState>{MaterialState.selected}), colorScheme.onPrimary); expect(m3.dayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.onSurface.withOpacity(0.38)); expect(m3.dayBackgroundColor?.resolve(<MaterialState>{}), null); expect(m3.dayBackgroundColor?.resolve(<MaterialState>{MaterialState.selected}), colorScheme.primary); expect(m3.dayOverlayColor?.resolve(<MaterialState>{}), null); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}), colorScheme.onPrimary.withOpacity(0.08)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}), colorScheme.onPrimary.withOpacity(0.12)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered, MaterialState.focused}), colorScheme.onPrimary.withOpacity(0.08)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered, MaterialState.pressed}), colorScheme.onPrimary.withOpacity(0.12)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m3.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered, MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m3.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary); expect(m3.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.primary.withOpacity(0.38)); expect(m3.todayBorder, BorderSide(color: colorScheme.primary)); expect(m3.yearStyle, textTheme.bodyLarge); expect(m3.yearForegroundColor?.resolve(<MaterialState>{}), colorScheme.onSurfaceVariant); expect(m3.yearForegroundColor?.resolve(<MaterialState>{MaterialState.selected}), colorScheme.onPrimary); expect(m3.yearForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.onSurfaceVariant.withOpacity(0.38)); expect(m3.yearBackgroundColor?.resolve(<MaterialState>{}), null); expect(m3.yearBackgroundColor?.resolve(<MaterialState>{MaterialState.selected}), colorScheme.primary); expect(m3.yearOverlayColor?.resolve(<MaterialState>{}), null); expect(m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}), colorScheme.onPrimary.withOpacity(0.08)); expect(m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}), colorScheme.onPrimary.withOpacity(0.12)); expect(m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m3.yearOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m3.rangePickerElevation, 0); expect(m3.rangePickerShape, const RoundedRectangleBorder()); expect(m3.rangePickerShadowColor, Colors.transparent); expect(m3.rangePickerSurfaceTintColor, Colors.transparent); expect(m3.rangeSelectionOverlayColor?.resolve(<MaterialState>{}), null); expect(m3.rangePickerHeaderBackgroundColor, Colors.transparent); expect(m3.rangePickerHeaderForegroundColor, colorScheme.onSurfaceVariant); expect(m3.rangePickerHeaderHeadlineStyle, textTheme.titleLarge); 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())); }); testWidgetsWithLeakTracking('DatePickerTheme.defaults M2 defaults', (WidgetTester tester) async { late final DatePickerThemeData m2; // M2 defaults late final ThemeData theme; late final ColorScheme colorScheme; late final TextTheme textTheme; await tester.pumpWidget( MaterialApp( theme: ThemeData(useMaterial3: false), home: Builder( builder: (BuildContext context) { m2 = DatePickerTheme.defaults(context); theme = Theme.of(context); colorScheme = theme.colorScheme; textTheme = theme.textTheme; return Container(); }, ), ), ); expect(m2.elevation, 24); expect(m2.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))); expect(m2.headerBackgroundColor, colorScheme.primary); expect(m2.headerForegroundColor, colorScheme.onPrimary); expect(m2.headerHeadlineStyle, textTheme.headlineSmall); expect(m2.headerHelpStyle, textTheme.labelSmall); expect(m2.weekdayStyle, textTheme.bodySmall?.apply(color: colorScheme.onSurface.withOpacity(0.60))); expect(m2.dayStyle, textTheme.bodySmall); expect(m2.dayForegroundColor?.resolve(<MaterialState>{}), colorScheme.onSurface); expect(m2.dayForegroundColor?.resolve(<MaterialState>{MaterialState.selected}), colorScheme.onPrimary); expect(m2.dayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.onSurface.withOpacity(0.38)); expect(m2.dayBackgroundColor?.resolve(<MaterialState>{}), null); expect(m2.dayBackgroundColor?.resolve(<MaterialState>{MaterialState.selected}), colorScheme.primary); expect(m2.dayOverlayColor?.resolve(<MaterialState>{}), null); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}), colorScheme.onPrimary.withOpacity(0.08)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}), colorScheme.onPrimary.withOpacity(0.12)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.pressed}), colorScheme.onPrimary.withOpacity(0.38)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered, MaterialState.focused}), colorScheme.onPrimary.withOpacity(0.08)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered, MaterialState.pressed}), colorScheme.onPrimary.withOpacity(0.38)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m2.dayOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m2.todayForegroundColor?.resolve(<MaterialState>{}), colorScheme.primary); expect(m2.todayForegroundColor?.resolve(<MaterialState>{MaterialState.disabled}), colorScheme.onSurface.withOpacity(0.38)); expect(m2.todayBorder, BorderSide(color: colorScheme.primary)); expect(m2.yearStyle, textTheme.bodyLarge); expect(m2.rangePickerBackgroundColor, colorScheme.surface); expect(m2.rangePickerElevation, 0); expect(m2.rangePickerShape, const RoundedRectangleBorder()); expect(m2.rangePickerShadowColor, Colors.transparent); expect(m2.rangePickerSurfaceTintColor, Colors.transparent); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{}), null); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.hovered}), colorScheme.onPrimary.withOpacity(0.08)); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.focused}), colorScheme.onPrimary.withOpacity(0.12)); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.selected, MaterialState.pressed}), colorScheme.onPrimary.withOpacity(0.38)); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.hovered}), colorScheme.onSurfaceVariant.withOpacity(0.08)); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.focused}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m2.rangeSelectionOverlayColor?.resolve(<MaterialState>{MaterialState.pressed}), colorScheme.onSurfaceVariant.withOpacity(0.12)); expect(m2.rangePickerHeaderBackgroundColor, colorScheme.primary); expect(m2.rangePickerHeaderForegroundColor, colorScheme.onPrimary); expect(m2.rangePickerHeaderHeadlineStyle, textTheme.headlineSmall); 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())); }); testWidgetsWithLeakTracking('Default DatePickerThemeData debugFillProperties', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); const DatePickerThemeData().debugFillProperties(builder); final List<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description, <String>[]); }); testWidgetsWithLeakTracking('DatePickerThemeData implements debugFillProperties', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); datePickerTheme.debugFillProperties(builder); final List<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description, equalsIgnoringHashCodes(<String>[ 'backgroundColor: Color(0xfffffff0)', 'elevation: 6.0', 'shadowColor: Color(0xfffffff1)', 'surfaceTintColor: Color(0xfffffff2)', 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)', 'headerBackgroundColor: Color(0xfffffff3)', 'headerForegroundColor: Color(0xfffffff4)', 'headerHeadlineStyle: TextStyle(inherit: true, size: 10.0)', 'headerHelpStyle: TextStyle(inherit: true, size: 11.0)', 'weekDayStyle: TextStyle(inherit: true, size: 12.0)', 'dayStyle: TextStyle(inherit: true, size: 13.0)', 'dayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff5))', 'dayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff6))', 'dayOverlayColor: MaterialStatePropertyAll(Color(0xfffffff7))', 'todayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff8))', 'todayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff9))', 'todayBorder: BorderSide(width: 3.0)', 'yearStyle: TextStyle(inherit: true, size: 13.0)', 'yearForegroundColor: MaterialStatePropertyAll(Color(0xfffffffa))', 'yearBackgroundColor: MaterialStatePropertyAll(Color(0xfffffffb))', 'yearOverlayColor: MaterialStatePropertyAll(Color(0xfffffffc))', 'rangePickerBackgroundColor: Color(0xfffffffd)', 'rangePickerElevation: 7.0', 'rangePickerShadowColor: Color(0xfffffffe)', 'rangePickerSurfaceTintColor: Color(0xffffffff)', 'rangePickerShape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)', 'rangePickerHeaderBackgroundColor: Color(0xffffff0f)', 'rangePickerHeaderForegroundColor: Color(0xffffff1f)', 'rangePickerHeaderHeadlineStyle: TextStyle(inherit: true, size: 14.0)', 'rangePickerHeaderHelpStyle: TextStyle(inherit: true, size: 15.0)', 'rangeSelectionBackgroundColor: Color(0xffffff2f)', 'rangeSelectionOverlayColor: MaterialStatePropertyAll(Color(0xffffff3f))', 'dividerColor: Color(0xffffff4f)', 'inputDecorationTheme: InputDecorationTheme#00000(fillColor: Color(0xffffff5f), border: UnderlineInputBorder())', 'cancelButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xffffff6f)))', 'confirmButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xffffff7f)))' ])); }); testWidgetsWithLeakTracking('DatePickerDialog uses ThemeData datePicker theme (calendar 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( 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 Text weekday = tester.widget<Text>(find.text('W')); expect(weekday.style?.color, datePickerTheme.weekdayStyle?.color); expect(weekday.style?.fontSize, datePickerTheme.weekdayStyle?.fontSize); final Text selectedDate = tester.widget<Text>(find.text('Wed, Jan 25')); expect(selectedDate.style?.color, datePickerTheme.headerForegroundColor); expect(selectedDate.style?.fontSize, datePickerTheme.headerHeadlineStyle?.fontSize); final Text day31 = tester.widget<Text>(find.text('31')); final BoxDecoration day31Decoration = findTextDecoration(tester, '31')!; expect(day31.style?.color, datePickerTheme.dayForegroundColor?.resolve(<MaterialState>{})); expect(day31.style?.fontSize, datePickerTheme.dayStyle?.fontSize); expect(day31Decoration.color, datePickerTheme.dayBackgroundColor?.resolve(<MaterialState>{})); final Text day24 = tester.widget<Text>(find.text('24')); // DatePickerDialog.currentDate final BoxDecoration day24Decoration = findTextDecoration(tester, '24')!; expect(day24.style?.fontSize, datePickerTheme.dayStyle?.fontSize); expect(day24.style?.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{})); expect(day24Decoration.color, datePickerTheme.todayBackgroundColor?.resolve(<MaterialState>{})); expect(day24Decoration.border?.top.width, datePickerTheme.todayBorder?.width); expect(day24Decoration.border?.bottom.width, datePickerTheme.todayBorder?.width); // Test the day overlay color. final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); final TestGesture gesture = await tester.createGesture( kind: PointerDeviceKind.mouse, ); await gesture.addPointer(); await gesture.moveTo(tester.getCenter(find.text('25'))); await tester.pumpAndSettle(); expect(inkFeatures, paints..circle(color: datePickerTheme.dayOverlayColor?.resolve(<MaterialState>{}))); // Show the year selector. await tester.tap(find.text('January 2023')); await tester.pumpAndSettle(); final Text year2022 = tester.widget<Text>(find.text('2022')); final BoxDecoration year2022Decoration = findTextDecoration(tester, '2022')!; expect(year2022.style?.fontSize, datePickerTheme.yearStyle?.fontSize); expect(year2022.style?.color, datePickerTheme.yearForegroundColor?.resolve(<MaterialState>{})); expect(year2022Decoration.color, datePickerTheme.yearBackgroundColor?.resolve(<MaterialState>{})); final Text year2023 = tester.widget<Text>(find.text('2023')); // DatePickerDialog.currentDate final BoxDecoration year2023Decoration = findTextDecoration(tester, '2023')!; expect(year2023.style?.fontSize, datePickerTheme.yearStyle?.fontSize); expect(year2023.style?.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{})); expect(year2023Decoration.color, datePickerTheme.todayBackgroundColor?.resolve(<MaterialState>{})); expect(year2023Decoration.border?.top.width, datePickerTheme.todayBorder?.width); expect(year2023Decoration.border?.bottom.width, datePickerTheme.todayBorder?.width); expect(year2023Decoration.border?.top.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{})); expect(year2023Decoration.border?.bottom.color, datePickerTheme.todayForegroundColor?.resolve(<MaterialState>{})); // Test the year overlay color. await gesture.moveTo(tester.getCenter(find.text('2024'))); await tester.pumpAndSettle(); expect(inkFeatures, paints..rect(color: datePickerTheme.yearOverlayColor?.resolve(<MaterialState>{}))); final ButtonStyle cancelButtonStyle = actionButtonStyle(tester, 'Cancel'); expect(cancelButtonStyle.toString(), equalsIgnoringHashCodes(datePickerTheme.cancelButtonStyle.toString())); final ButtonStyle confirmButtonStyle = actionButtonStyle(tester, 'OK'); expect(confirmButtonStyle.toString(), equalsIgnoringHashCodes(datePickerTheme.confirmButtonStyle.toString())); }); testWidgetsWithLeakTracking('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); 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())); }); testWidgetsWithLeakTracking('DateRangePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( theme: ThemeData( datePickerTheme: datePickerTheme, useMaterial3: true, ), home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: DateRangePickerDialog( firstDate: DateTime(2023), lastDate: DateTime(2023, DateTime.january, 31), initialDateRange: DateTimeRange( start: DateTime(2023, DateTime.january, 17), end: DateTime(2023, DateTime.january, 20), ), currentDate: DateTime(2023, DateTime.january, 23), ), ), ), ), ), ); final Material material = findDialogMaterial(tester); expect(material.color, datePickerTheme.backgroundColor); //!! expect(tester.widget<Scaffold>(find.byType(Scaffold)).backgroundColor, datePickerTheme.rangePickerBackgroundColor); expect(material.elevation, datePickerTheme.rangePickerElevation); expect(material.shadowColor, datePickerTheme.rangePickerShadowColor); expect(material.surfaceTintColor, datePickerTheme.rangePickerSurfaceTintColor); expect(material.shape, datePickerTheme.rangePickerShape); final AppBar appBar = tester.widget<AppBar>(find.byType(AppBar)); expect(appBar.backgroundColor, datePickerTheme.rangePickerHeaderBackgroundColor); final Text selectRange = tester.widget<Text>(find.text('Select range')); expect(selectRange.style?.color, datePickerTheme.rangePickerHeaderForegroundColor); expect(selectRange.style?.fontSize, datePickerTheme.rangePickerHeaderHelpStyle?.fontSize); final Text selectedDate = tester.widget<Text>(find.text('Jan 17')); expect(selectedDate.style?.color, datePickerTheme.rangePickerHeaderForegroundColor); expect(selectedDate.style?.fontSize, datePickerTheme.rangePickerHeaderHeadlineStyle?.fontSize); // Test the day overlay color. final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); final TestGesture gesture = await tester.createGesture( kind: PointerDeviceKind.mouse, ); await gesture.addPointer(); await gesture.moveTo(tester.getCenter(find.text('16'))); await tester.pumpAndSettle(); expect(inkFeatures, paints..circle(color: datePickerTheme.dayOverlayColor?.resolve(<MaterialState>{}))); // Test the range selection overlay color. await gesture.moveTo(tester.getCenter(find.text('18'))); await tester.pumpAndSettle(); expect(inkFeatures, paints..circle(color: datePickerTheme.rangeSelectionOverlayColor?.resolve(<MaterialState>{}))); }); testWidgetsWithLeakTracking('Dividers use DatePickerThemeData.dividerColor', (WidgetTester tester) async { Future<void> showPicker(WidgetTester tester, Size size) async { tester.view.physicalSize = size; tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); await tester.pumpWidget( MaterialApp( theme: ThemeData( datePickerTheme: datePickerTheme, useMaterial3: true, ), home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: DatePickerDialog( initialDate: DateTime(2023, DateTime.january, 25), firstDate: DateTime(2022), lastDate: DateTime(2024, DateTime.december, 31), currentDate: DateTime(2023, DateTime.january, 24), ), ), ), ), ), ); } await showPicker(tester, wideWindowSize); // Test vertical divider. final VerticalDivider verticalDivider = tester.widget(find.byType(VerticalDivider)); expect(verticalDivider.color, datePickerTheme.dividerColor); // Test portrait layout. await showPicker(tester, narrowWindowSize); // Test horizontal divider. final Divider horizontalDivider = tester.widget(find.byType(Divider)); expect(horizontalDivider.color, datePickerTheme.dividerColor); }); testWidgetsWithLeakTracking( '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()); }); testWidgetsWithLeakTracking('DatePickerDialog resolves DatePickerTheme.dayOverlayColor states', (WidgetTester tester) async { final MaterialStateProperty<Color> dayOverlayColor = MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) { if (states.contains(MaterialState.hovered)) { return const Color(0xff00ff00); } if (states.contains(MaterialState.focused)) { return const Color(0xffff00ff); } if (states.contains(MaterialState.pressed)) { return const Color(0xffffff00); } return Colors.transparent; }); await tester.pumpWidget( MaterialApp( theme: ThemeData( datePickerTheme: DatePickerThemeData( dayOverlayColor: dayOverlayColor, ), useMaterial3: true, ), home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: Focus( child: DatePickerDialog( initialDate: DateTime(2023, DateTime.january, 25), firstDate: DateTime(2022), lastDate: DateTime(2024, DateTime.december, 31), currentDate: DateTime(2023, DateTime.january, 24), ), ), ), ), ), ), ); // Test the hover overlay color. final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); final TestGesture gesture = await tester.createGesture( kind: PointerDeviceKind.mouse, ); await gesture.addPointer(); await gesture.moveTo(tester.getCenter(find.text('20'))); await tester.pumpAndSettle(); expect( inkFeatures, paints ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered})), ); // Test the pressed overlay color. await gesture.down(tester.getCenter(find.text('20'))); await tester.pumpAndSettle(); if (kIsWeb) { // An extra circle is painted on the web for the hovered state. expect( inkFeatures, paints ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.pressed})), ); } else { expect( inkFeatures, paints ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.pressed})), ); } await gesture.removePointer(); await tester.pumpAndSettle(); // Focus day selection. for (int i = 0; i < 5; i++) { await tester.sendKeyEvent(LogicalKeyboardKey.tab); await tester.pumpAndSettle(); } // Test the focused overlay color. expect( inkFeatures, paints ..circle(color: dayOverlayColor.resolve(<MaterialState>{MaterialState.focused})), ); }); testWidgetsWithLeakTracking('DatePickerDialog resolves DatePickerTheme.yearOverlayColor states', (WidgetTester tester) async { final MaterialStateProperty<Color> yearOverlayColor = MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) { if (states.contains(MaterialState.hovered)) { return const Color(0xff00ff00); } if (states.contains(MaterialState.focused)) { return const Color(0xffff00ff); } if (states.contains(MaterialState.pressed)) { return const Color(0xffffff00); } return Colors.transparent; }); await tester.pumpWidget( MaterialApp( theme: ThemeData( datePickerTheme: DatePickerThemeData( yearOverlayColor: yearOverlayColor, ), useMaterial3: true, ), home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: Focus( child: DatePickerDialog( initialDate: DateTime(2023, DateTime.january, 25), firstDate: DateTime(2022), lastDate: DateTime(2024, DateTime.december, 31), currentDate: DateTime(2023, DateTime.january, 24), initialCalendarMode: DatePickerMode.year, ), ), ), ), ), ), ); // Test the hover overlay color. final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); final TestGesture gesture = await tester.createGesture( kind: PointerDeviceKind.mouse, ); await gesture.addPointer(); await gesture.moveTo(tester.getCenter(find.text('2022'))); await tester.pumpAndSettle(); expect( inkFeatures, paints ..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.hovered})), ); // Test the pressed overlay color. await gesture.down(tester.getCenter(find.text('2022'))); await tester.pumpAndSettle(); expect( inkFeatures, paints ..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.pressed})), ); await gesture.removePointer(); await tester.pumpAndSettle(); // Focus year selection. for (int i = 0; i < 3; i++) { await tester.sendKeyEvent(LogicalKeyboardKey.tab); await tester.pumpAndSettle(); } // Test the focused overlay color. expect( inkFeatures, paints ..rect(color: yearOverlayColor.resolve(<MaterialState>{MaterialState.focused})), ); }); testWidgetsWithLeakTracking('DateRangePickerDialog resolves DatePickerTheme.rangeSelectionOverlayColor states', (WidgetTester tester) async { final MaterialStateProperty<Color> rangeSelectionOverlayColor = MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) { if (states.contains(MaterialState.hovered)) { return const Color(0xff00ff00); } if (states.contains(MaterialState.pressed)) { return const Color(0xffffff00); } return Colors.transparent; }); await tester.pumpWidget( MaterialApp( theme: ThemeData( datePickerTheme: DatePickerThemeData( rangeSelectionOverlayColor: rangeSelectionOverlayColor, ), useMaterial3: true, ), home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: DateRangePickerDialog( firstDate: DateTime(2023), lastDate: DateTime(2023, DateTime.january, 31), initialDateRange: DateTimeRange( start: DateTime(2023, DateTime.january, 17), end: DateTime(2023, DateTime.january, 20), ), currentDate: DateTime(2023, DateTime.january, 23), ), ), ), ), ), ); // Test the hover overlay color. final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); final TestGesture gesture = await tester.createGesture( kind: PointerDeviceKind.mouse, ); await gesture.addPointer(); await gesture.moveTo(tester.getCenter(find.text('18'))); await tester.pumpAndSettle(); expect( inkFeatures, paints ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered})), ); // Test the pressed overlay color. await gesture.down(tester.getCenter(find.text('18'))); await tester.pumpAndSettle(); if (kIsWeb) { // An extra circle is painted on the web for the hovered state. expect( inkFeatures, paints ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.pressed})), ); } else { expect( inkFeatures, paints ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.hovered})) ..circle(color: rangeSelectionOverlayColor.resolve(<MaterialState>{MaterialState.pressed})), ); } }); }