// 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})),
      );
    }
  });
}