Migrate the Material Date pickers to M3 Reprise (#119033)

diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart
index fa156d8..9f71ec6 100644
--- a/dev/tools/gen_defaults/bin/gen_defaults.dart
+++ b/dev/tools/gen_defaults/bin/gen_defaults.dart
@@ -27,6 +27,7 @@
 import 'package:gen_defaults/card_template.dart';
 import 'package:gen_defaults/checkbox_template.dart';
 import 'package:gen_defaults/color_scheme_template.dart';
+import 'package:gen_defaults/date_picker_template.dart';
 import 'package:gen_defaults/dialog_template.dart';
 import 'package:gen_defaults/divider_template.dart';
 import 'package:gen_defaults/drawer_template.dart';
@@ -147,6 +148,7 @@
   CardTemplate('Card', '$materialLib/card.dart', tokens).updateFile();
   CheckboxTemplate('Checkbox', '$materialLib/checkbox.dart', tokens).updateFile();
   ColorSchemeTemplate('ColorScheme', '$materialLib/theme_data.dart', tokens).updateFile();
+  DatePickerTemplate('DatePicker', '$materialLib/date_picker_theme.dart', tokens).updateFile();
   DialogFullscreenTemplate('DialogFullscreen', '$materialLib/dialog.dart', tokens).updateFile();
   DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
   DividerTemplate('Divider', '$materialLib/divider.dart', tokens).updateFile();
diff --git a/dev/tools/gen_defaults/lib/date_picker_template.dart b/dev/tools/gen_defaults/lib/date_picker_template.dart
new file mode 100644
index 0000000..6b684cf
--- /dev/null
+++ b/dev/tools/gen_defaults/lib/date_picker_template.dart
@@ -0,0 +1,238 @@
+// 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 'template.dart';
+
+class DatePickerTemplate extends TokenTemplate {
+  const DatePickerTemplate(super.blockName, super.fileName, super.tokens, {
+    super.colorSchemePrefix = '_colors.',
+    super.textThemePrefix = '_textTheme.'
+  });
+
+  String _layerOpacity(String layerToken) {
+    if (tokens.containsKey(layerToken)) {
+      final String? layerValue = tokens[layerToken] as String?;
+      if (tokens.containsKey(layerValue)) {
+        final String? opacityValue = opacity(layerValue!);
+        if (opacityValue != null) {
+          return '.withOpacity($opacityValue)';
+        }
+      }
+    }
+    return '';
+  }
+
+  String _stateColor(String componentToken, String? type, String state) {
+    final String baseColor = color(
+      type != null
+        ? '$componentToken.$type.$state.state-layer.color'
+        : '$componentToken.$state.state-layer.color',
+      ''
+    );
+    if (baseColor.isEmpty) {
+      return 'null';
+    }
+    final String opacity = _layerOpacity('$componentToken.$state.state-layer.opacity');
+    return '$baseColor$opacity';
+  }
+
+  @override
+  String generate() => '''
+class _${blockName}DefaultsM3 extends DatePickerThemeData {
+  _${blockName}DefaultsM3(this.context)
+    : super(
+        elevation: ${elevation("md.comp.date-picker.modal.container")},
+        shape: ${shape("md.comp.date-picker.modal.container")},
+        rangePickerElevation: ${elevation("md.comp.date-picker.modal.range-selection.container")},
+        rangePickerShape: ${shape("md.comp.date-picker.modal.range-selection.container")},
+      );
+
+  final BuildContext context;
+  late final ThemeData _theme = Theme.of(context);
+  late final ColorScheme _colors = _theme.colorScheme;
+  late final TextTheme _textTheme = _theme.textTheme;
+
+  @override
+  Color? get backgroundColor => ${componentColor("md.comp.date-picker.modal.container")};
+
+  @override
+  Color? get shadowColor => ${colorOrTransparent("md.comp.date-picker.modal.container.shadow-color")};
+
+  @override
+  Color? get surfaceTintColor => ${colorOrTransparent("md.comp.date-picker.modal.container.surface-tint-layer.color")};
+
+  @override
+  Color? get headerBackgroundColor => ${colorOrTransparent("md.comp.date-picker.modal.header.container.color")};
+
+  @override
+  Color? get headerForegroundColor => ${colorOrTransparent("md.comp.date-picker.modal.header.headline.color")};
+
+  @override
+  TextStyle? get headerHeadlineStyle => ${textStyle("md.comp.date-picker.modal.header.headline")};
+
+  @override
+  TextStyle? get headerHelpStyle => ${textStyle("md.comp.date-picker.modal.header.supporting-text")};
+
+  @override
+  TextStyle? get weekdayStyle => ${textStyle("md.comp.date-picker.modal.weekdays.label-text")}?.apply(
+    color: ${componentColor("md.comp.date-picker.modal.weekdays.label-text")},
+  );
+
+  @override
+  TextStyle? get dayStyle => ${textStyle("md.comp.date-picker.modal.date.label-text")};
+
+  @override
+  MaterialStateProperty<Color?>? get dayForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return ${componentColor('md.comp.date-picker.modal.date.selected.label-text')};
+      } else if (states.contains(MaterialState.disabled)) {
+        return ${componentColor('md.comp.date-picker.modal.date.unselected.label-text')}.withOpacity(0.38);
+      }
+      return ${componentColor('md.comp.date-picker.modal.date.unselected.label-text')};
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get dayBackgroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return ${componentColor('md.comp.date-picker.modal.date.selected.container')};
+      }
+      return ${componentColor('md.comp.date-picker.modal.date.unselected.container')};
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get dayOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        if (states.contains(MaterialState.hovered)) {
+          return ${_stateColor('md.comp.date-picker.modal.date', 'selected', 'hover')};
+        }
+        if (states.contains(MaterialState.focused)) {
+          return ${_stateColor('md.comp.date-picker.modal.date', 'selected', 'focus')};
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return ${_stateColor('md.comp.date-picker.modal.date', 'selected', 'pressed')};
+        }
+      } else {
+        if (states.contains(MaterialState.hovered)) {
+          return ${_stateColor('md.comp.date-picker.modal.date', 'unselected', 'hover')};
+        }
+        if (states.contains(MaterialState.focused)) {
+          return ${_stateColor('md.comp.date-picker.modal.date', 'unselected', 'focus')};
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return ${_stateColor('md.comp.date-picker.modal.date', 'unselected', 'pressed')};
+        }
+      }
+      return null;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get todayForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return ${componentColor('md.comp.date-picker.modal.date.selected.label-text')};
+      } else if (states.contains(MaterialState.disabled)) {
+        return ${componentColor('md.comp.date-picker.modal.date.today.label-text')}.withOpacity(0.38);
+      }
+      return ${componentColor('md.comp.date-picker.modal.date.today.label-text')};
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get todayBackgroundColor => dayBackgroundColor;
+
+  @override
+  BorderSide? get todayBorder => ${border('md.comp.date-picker.modal.date.today.container.outline')};
+
+  @override
+  TextStyle? get yearStyle => ${textStyle("md.comp.date-picker.modal.year-selection.year.label-text")};
+
+  @override
+  MaterialStateProperty<Color?>? get yearForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return ${componentColor('md.comp.date-picker.modal.year-selection.year.selected.label-text')};
+      } else if (states.contains(MaterialState.disabled)) {
+        return ${componentColor('md.comp.date-picker.modal.year-selection.year.unselected.label-text')}.withOpacity(0.38);
+      }
+      return ${componentColor('md.comp.date-picker.modal.year-selection.year.unselected.label-text')};
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get yearBackgroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return ${componentColor('md.comp.date-picker.modal.year-selection.year.selected.container')};
+      }
+      return ${componentColor('md.comp.date-picker.modal.year-selection.year.unselected.container')};
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get yearOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        if (states.contains(MaterialState.hovered)) {
+          return ${_stateColor('md.comp.date-picker.modal.year-selection.year', 'selected', 'hover')};
+        }
+        if (states.contains(MaterialState.focused)) {
+          return ${_stateColor('md.comp.date-picker.modal.year-selection.year', 'selected', 'focus')};
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return ${_stateColor('md.comp.date-picker.modal.year-selection.year', 'selected', 'pressed')};
+        }
+      } else {
+        if (states.contains(MaterialState.hovered)) {
+          return ${_stateColor('md.comp.date-picker.modal.year-selection.year', 'unselected', 'hover')};
+        }
+        if (states.contains(MaterialState.focused)) {
+          return ${_stateColor('md.comp.date-picker.modal.year-selection.year', 'unselected', 'focus')};
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return ${_stateColor('md.comp.date-picker.modal.year-selection.year', 'unselected', 'pressed')};
+        }
+      }
+      return null;
+    });
+
+    @override
+    Color? get rangePickerShadowColor => ${colorOrTransparent("md.comp.date-picker.modal.range-selection.container.shadow-color")};
+
+    @override
+    Color? get rangePickerSurfaceTintColor => ${colorOrTransparent("md.comp.date-picker.modal.range-selection.container.surface-tint-layer.color")};
+
+    @override
+    Color? get rangeSelectionBackgroundColor => ${colorOrTransparent("md.comp.date-picker.modal.range-selection.active-indicator.container.color")};
+
+  @override
+  MaterialStateProperty<Color?>? get rangeSelectionOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.hovered)) {
+        return ${_stateColor('md.comp.date-picker.modal.range-selection.date.in-range.', null, 'hover')};
+      }
+      if (states.contains(MaterialState.focused)) {
+        return ${_stateColor('md.comp.date-picker.modal.range-selection.date.in-range.', null, 'focus')};
+      }
+      if (states.contains(MaterialState.pressed)) {
+        return ${_stateColor('md.comp.date-picker.modal.range-selection.date.in-range.', null, 'pressed')};
+      }
+      return null;
+    });
+
+  @override
+  Color? get rangePickerHeaderBackgroundColor => ${colorOrTransparent("md.comp.date-picker.modal.header.container.color")};
+
+  @override
+  Color? get rangePickerHeaderForegroundColor => ${colorOrTransparent("md.comp.date-picker.modal.header.headline.color")};
+
+  @override
+  TextStyle? get rangePickerHeaderHeadlineStyle => ${textStyle("md.comp.date-picker.modal.range-selection.header.headline")};
+
+  @override
+  TextStyle? get rangePickerHeaderHelpStyle => ${textStyle("md.comp.date-picker.modal.range-selection.month.subhead")};
+
+
+}
+''';
+}
diff --git a/packages/flutter/lib/material.dart b/packages/flutter/lib/material.dart
index 1943379..fb07102 100644
--- a/packages/flutter/lib/material.dart
+++ b/packages/flutter/lib/material.dart
@@ -65,6 +65,7 @@
 export 'src/material/data_table_theme.dart';
 export 'src/material/date.dart';
 export 'src/material/date_picker.dart';
+export 'src/material/date_picker_theme.dart';
 export 'src/material/debug.dart';
 export 'src/material/desktop_text_selection.dart';
 export 'src/material/desktop_text_selection_toolbar.dart';
diff --git a/packages/flutter/lib/src/material/calendar_date_picker.dart b/packages/flutter/lib/src/material/calendar_date_picker.dart
index 189d240..1e49a9b 100644
--- a/packages/flutter/lib/src/material/calendar_date_picker.dart
+++ b/packages/flutter/lib/src/material/calendar_date_picker.dart
@@ -11,12 +11,14 @@
 
 import 'color_scheme.dart';
 import 'date.dart';
+import 'date_picker_theme.dart';
 import 'debug.dart';
 import 'divider.dart';
 import 'icon_button.dart';
 import 'icons.dart';
 import 'ink_well.dart';
 import 'material_localizations.dart';
+import 'material_state.dart';
 import 'text_theme.dart';
 import 'theme.dart';
 
@@ -279,7 +281,7 @@
             firstDate: widget.firstDate,
             lastDate: widget.lastDate,
             initialDate: _currentDisplayedMonthDate,
-            selectedDate: _selectedDate,
+            selectedDate: _currentDisplayedMonthDate,
             onChanged: _handleYearChanged,
           ),
         );
@@ -920,18 +922,11 @@
 
   @override
   Widget build(BuildContext context) {
-    final ColorScheme colorScheme = Theme.of(context).colorScheme;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
-    final TextTheme textTheme = Theme.of(context).textTheme;
-    final TextStyle? headerStyle = textTheme.bodySmall?.apply(
-      color: colorScheme.onSurface.withOpacity(0.60),
-    );
-    final TextStyle dayStyle = textTheme.bodySmall!;
-    final Color enabledDayColor = colorScheme.onSurface.withOpacity(0.87);
-    final Color disabledDayColor = colorScheme.onSurface.withOpacity(0.38);
-    final Color selectedDayColor = colorScheme.onPrimary;
-    final Color selectedDayBackground = colorScheme.primary;
-    final Color todayColor = colorScheme.primary;
+    final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
+    final TextStyle? weekdayStyle = datePickerTheme.weekdayStyle ?? defaults.weekdayStyle;
+    final TextStyle? dayStyle = datePickerTheme.dayStyle ?? defaults.dayStyle;
 
     final int year = widget.displayedMonth.year;
     final int month = widget.displayedMonth.month;
@@ -939,7 +934,19 @@
     final int daysInMonth = DateUtils.getDaysInMonth(year, month);
     final int dayOffset = DateUtils.firstDayOffset(year, month, localizations);
 
-    final List<Widget> dayItems = _dayHeaders(headerStyle, localizations);
+    T? effectiveValue<T>(T? Function(DatePickerThemeData? theme) getProperty) {
+      return getProperty(datePickerTheme) ?? getProperty(defaults);
+    }
+
+    T? resolve<T>(MaterialStateProperty<T>? Function(DatePickerThemeData? theme) getProperty, Set<MaterialState> states) {
+      return effectiveValue(
+        (DatePickerThemeData? theme) {
+          return getProperty(theme)?.resolve(states);
+        },
+      );
+    }
+
+    final List<Widget> dayItems = _dayHeaders(weekdayStyle, localizations);
     // 1-based day of month, e.g. 1-31 for January, and 1-29 for February on
     // a leap year.
     int day = -dayOffset;
@@ -949,43 +956,42 @@
         dayItems.add(Container());
       } else {
         final DateTime dayToBuild = DateTime(year, month, day);
-        final bool isDisabled = dayToBuild.isAfter(widget.lastDate) ||
-            dayToBuild.isBefore(widget.firstDate) ||
-            (widget.selectableDayPredicate != null && !widget.selectableDayPredicate!(dayToBuild));
+        final bool isDisabled =
+          dayToBuild.isAfter(widget.lastDate) ||
+          dayToBuild.isBefore(widget.firstDate) ||
+          (widget.selectableDayPredicate != null && !widget.selectableDayPredicate!(dayToBuild));
         final bool isSelectedDay = DateUtils.isSameDay(widget.selectedDate, dayToBuild);
         final bool isToday = DateUtils.isSameDay(widget.currentDate, dayToBuild);
         final String semanticLabelSuffix = isToday ? ', ${localizations.currentDateLabel}' : '';
 
-        BoxDecoration? decoration;
-        Color dayColor = enabledDayColor;
-        if (isSelectedDay) {
-          // The selected day gets a circle background highlight, and a
-          // contrasting text color.
-          dayColor = selectedDayColor;
-          decoration = BoxDecoration(
-            color: selectedDayBackground,
-            shape: BoxShape.circle,
-          );
-        } else if (isToday) {
-          // The current day gets a different text color (if enabled) and a circle stroke
-          // border.
-          if (isDisabled) {
-            dayColor = disabledDayColor;
-          } else {
-            dayColor = todayColor;
-          }
-          decoration = BoxDecoration(
-            border: Border.all(color: dayColor),
-            shape: BoxShape.circle,
-          );
-        } else if (isDisabled) {
-          dayColor = disabledDayColor;
-        }
+        final Set<MaterialState> states = <MaterialState>{
+          if (isDisabled) MaterialState.disabled,
+          if (isSelectedDay) MaterialState.selected,
+        };
+
+        final Color? dayForegroundColor = resolve<Color?>((DatePickerThemeData? theme) => isToday ? theme?.todayForegroundColor : theme?.dayForegroundColor, states);
+        final Color? dayBackgroundColor = resolve<Color?>((DatePickerThemeData? theme) => isToday ? theme?.todayBackgroundColor : theme?.dayBackgroundColor, states);
+        final MaterialStateProperty<Color?> dayOverlayColor = MaterialStateProperty.resolveWith<Color?>(
+          (Set<MaterialState> states) => effectiveValue((DatePickerThemeData? theme) => theme?.dayOverlayColor?.resolve(states)),
+        );
+        final BoxDecoration decoration = isToday
+          ? BoxDecoration(
+              color: dayBackgroundColor,
+              border: Border.fromBorderSide(
+                (datePickerTheme.todayBorder ?? defaults.todayBorder!)
+                  .copyWith(color: dayForegroundColor)
+              ),
+              shape: BoxShape.circle,
+            )
+          : BoxDecoration(
+              color: dayBackgroundColor,
+              shape: BoxShape.circle,
+            );
 
         Widget dayWidget = Container(
           decoration: decoration,
           child: Center(
-            child: Text(localizations.formatDecimal(day), style: dayStyle.apply(color: dayColor)),
+            child: Text(localizations.formatDecimal(day), style: dayStyle?.apply(color: dayForegroundColor)),
           ),
         );
 
@@ -998,7 +1004,8 @@
             focusNode: _dayFocusNodes[day - 1],
             onTap: () => widget.onChanged(dayToBuild),
             radius: _dayPickerRowHeight / 2 + 4,
-            splashColor: selectedDayBackground.withOpacity(0.38),
+            statesController: MaterialStatesController(states),
+            overlayColor: dayOverlayColor,
             child: Semantics(
               // We want the day of month to be spoken first irrespective of the
               // locale-specific preferences or TextDirection. This is because
@@ -1150,8 +1157,20 @@
   }
 
   Widget _buildYearItem(BuildContext context, int index) {
-    final ColorScheme colorScheme = Theme.of(context).colorScheme;
-    final TextTheme textTheme = Theme.of(context).textTheme;
+    final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
+
+    T? effectiveValue<T>(T? Function(DatePickerThemeData? theme) getProperty) {
+      return getProperty(datePickerTheme) ?? getProperty(defaults);
+    }
+
+    T? resolve<T>(MaterialStateProperty<T>? Function(DatePickerThemeData? theme) getProperty, Set<MaterialState> states) {
+      return effectiveValue(
+        (DatePickerThemeData? theme) {
+          return getProperty(theme)?.resolve(states);
+        },
+      );
+    }
 
     // Backfill the _YearPicker with disabled years if necessary.
     final int offset = _itemCount < minYears ? (minYears - _itemCount) ~/ 2 : 0;
@@ -1162,33 +1181,32 @@
     const double decorationHeight = 36.0;
     const double decorationWidth = 72.0;
 
-    final Color textColor;
-    if (isSelected) {
-      textColor = colorScheme.onPrimary;
-    } else if (isDisabled) {
-      textColor = colorScheme.onSurface.withOpacity(0.38);
-    } else if (isCurrentYear) {
-      textColor = colorScheme.primary;
-    } else {
-      textColor = colorScheme.onSurface.withOpacity(0.87);
-    }
-    final TextStyle? itemStyle = textTheme.bodyLarge?.apply(color: textColor);
+    final Set<MaterialState> states = <MaterialState>{
+      if (isDisabled) MaterialState.disabled,
+      if (isSelected) MaterialState.selected,
+    };
 
-    BoxDecoration? decoration;
-    if (isSelected) {
-      decoration = BoxDecoration(
-        color: colorScheme.primary,
-        borderRadius: BorderRadius.circular(decorationHeight / 2),
+    final Color? textColor = resolve<Color?>((DatePickerThemeData? theme) => isCurrentYear ? theme?.todayForegroundColor : theme?.yearForegroundColor, states);
+    final Color? background = resolve<Color?>((DatePickerThemeData? theme) => isCurrentYear ? theme?.todayBackgroundColor : theme?.yearBackgroundColor, states);
+    final MaterialStateProperty<Color?> overlayColor =
+      MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) =>
+        effectiveValue((DatePickerThemeData? theme) => theme?.dayOverlayColor?.resolve(states)),
       );
-    } else if (isCurrentYear && !isDisabled) {
-      decoration = BoxDecoration(
-        border: Border.all(
-          color: colorScheme.primary,
-        ),
-        borderRadius: BorderRadius.circular(decorationHeight / 2),
-      );
-    }
 
+    BoxBorder? border;
+    if (isCurrentYear) {
+      final BorderSide? todayBorder = datePickerTheme.todayBorder ?? defaults.todayBorder;
+      if (todayBorder != null) {
+        border = Border.fromBorderSide(todayBorder.copyWith(color: textColor));
+      }
+    }
+    final BoxDecoration decoration = BoxDecoration(
+      border: border,
+      color: background,
+      borderRadius: BorderRadius.circular(decorationHeight / 2),
+    );
+
+    final TextStyle? itemStyle = (datePickerTheme.yearStyle ?? defaults.yearStyle)?.apply(color: textColor);
     Widget yearItem = Center(
       child: Container(
         decoration: decoration,
@@ -1212,6 +1230,8 @@
       yearItem = InkWell(
         key: ValueKey<int>(year),
         onTap: () => widget.onChanged(DateTime(year, widget.initialDate.month)),
+        statesController: MaterialStatesController(states),
+        overlayColor: overlayColor,
         child: yearItem,
       );
     }
diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart
index cb963f5..db9fd8b 100644
--- a/packages/flutter/lib/src/material/date_picker.dart
+++ b/packages/flutter/lib/src/material/date_picker.dart
@@ -10,9 +10,12 @@
 
 import 'app_bar.dart';
 import 'back_button.dart';
+import 'button_style.dart';
 import 'calendar_date_picker.dart';
 import 'color_scheme.dart';
+import 'colors.dart';
 import 'date.dart';
+import 'date_picker_theme.dart';
 import 'debug.dart';
 import 'dialog.dart';
 import 'dialog_theme.dart';
@@ -25,15 +28,20 @@
 import 'input_decorator.dart';
 import 'material.dart';
 import 'material_localizations.dart';
+import 'material_state.dart';
 import 'scaffold.dart';
 import 'text_button.dart';
 import 'text_field.dart';
 import 'text_theme.dart';
 import 'theme.dart';
 
-const Size _calendarPortraitDialogSize = Size(330.0, 518.0);
+// The M3 sizes are coming from the tokens, but are hand coded,
+// as the current token DB does not contain landscape versions.
+const Size _calendarPortraitDialogSizeM2 = Size(330.0, 518.0);
+const Size _calendarPortraitDialogSizeM3 = Size(328.0, 512.0);
 const Size _calendarLandscapeDialogSize = Size(496.0, 346.0);
-const Size _inputPortraitDialogSize = Size(330.0, 270.0);
+const Size _inputPortraitDialogSizeM2 = Size(330.0, 270.0);
+const Size _inputPortraitDialogSizeM3 = Size(328.0, 270.0);
 const Size _inputLandscapeDialogSize = Size(496, 160.0);
 const Size _inputRangeLandscapeDialogSize = Size(496, 164.0);
 const Duration _dialogSizeAnimationDuration = Duration(milliseconds: 200);
@@ -412,13 +420,15 @@
   }
 
   Size _dialogSize(BuildContext context) {
+    final bool useMaterial3 = Theme.of(context).useMaterial3;
     final Orientation orientation = MediaQuery.orientationOf(context);
+
     switch (_entryMode.value) {
       case DatePickerEntryMode.calendar:
       case DatePickerEntryMode.calendarOnly:
         switch (orientation) {
           case Orientation.portrait:
-            return _calendarPortraitDialogSize;
+            return useMaterial3 ? _calendarPortraitDialogSizeM3 : _calendarPortraitDialogSizeM2;
           case Orientation.landscape:
             return _calendarLandscapeDialogSize;
         }
@@ -426,7 +436,7 @@
       case DatePickerEntryMode.inputOnly:
         switch (orientation) {
           case Orientation.portrait:
-            return _inputPortraitDialogSize;
+            return useMaterial3 ? _inputPortraitDialogSizeM3 : _inputPortraitDialogSizeM2;
           case Orientation.landscape:
             return _inputLandscapeDialogSize;
         }
@@ -441,21 +451,28 @@
   @override
   Widget build(BuildContext context) {
     final ThemeData theme = Theme.of(context);
-    final ColorScheme colorScheme = theme.colorScheme;
+    final bool useMaterial3 = theme.useMaterial3;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
     final Orientation orientation = MediaQuery.orientationOf(context);
+    final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
     final TextTheme textTheme = theme.textTheme;
+
     // Constrain the textScaleFactor to the largest supported value to prevent
     // layout issues.
     final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.3);
+    final Color? headerForegroundColor = datePickerTheme.headerForegroundColor ?? defaults.headerForegroundColor;
+    final TextStyle? headlineStyle = useMaterial3
+      ? (datePickerTheme.headerHeadlineStyle ?? defaults.headerHeadlineStyle)?.copyWith(
+          color: headerForegroundColor,
+        )
+      // Material2 has support for landscape and the current M3 spec doesn't
+      // address this layout, so handling it seperately here.
+      : (orientation == Orientation.landscape
+        ? textTheme.headlineSmall?.copyWith(color: headerForegroundColor)
+        : textTheme.headlineMedium?.copyWith(color: headerForegroundColor));
 
     final String dateText = localizations.formatMediumDate(_selectedDate.value);
-    final Color onPrimarySurface = colorScheme.brightness == Brightness.light
-      ? colorScheme.onPrimary
-      : colorScheme.onSurface;
-    final TextStyle? dateStyle = orientation == Orientation.landscape
-      ? textTheme.headlineSmall?.copyWith(color: onPrimarySurface)
-      : textTheme.headlineMedium?.copyWith(color: onPrimarySurface);
 
     final Widget actions = Container(
       alignment: AlignmentDirectional.centerEnd,
@@ -467,7 +484,7 @@
           TextButton(
             onPressed: _handleCancel,
             child: Text(widget.cancelText ?? (
-              theme.useMaterial3
+              useMaterial3
                 ? localizations.cancelButtonLabel
                 : localizations.cancelButtonLabel.toUpperCase()
             )),
@@ -533,8 +550,8 @@
       case DatePickerEntryMode.calendar:
         picker = calendarDatePicker();
         entryModeButton = IconButton(
-          icon: const Icon(Icons.edit),
-          color: onPrimarySurface,
+          icon:  Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit),
+          color: headerForegroundColor,
           tooltip: localizations.inputDateModeButtonLabel,
           onPressed: _handleEntryModeToggle,
         );
@@ -549,7 +566,7 @@
         picker = inputDatePicker();
         entryModeButton = IconButton(
           icon: const Icon(Icons.calendar_today),
-          color: onPrimarySurface,
+          color: headerForegroundColor,
           tooltip: localizations.calendarModeButtonLabel,
           onPressed: _handleEntryModeToggle,
         );
@@ -563,19 +580,29 @@
 
     final Widget header = _DatePickerHeader(
       helpText: widget.helpText ?? (
-        Theme.of(context).useMaterial3
+        useMaterial3
           ? localizations.datePickerHelpText
           : localizations.datePickerHelpText.toUpperCase()
       ),
       titleText: dateText,
-      titleStyle: dateStyle,
+      titleStyle: headlineStyle,
       orientation: orientation,
       isShort: orientation == Orientation.landscape,
       entryModeButton: entryModeButton,
     );
 
     final Size dialogSize = _dialogSize(context) * textScaleFactor;
+    final DialogTheme dialogTheme = theme.dialogTheme;
     return Dialog(
+      backgroundColor: datePickerTheme.backgroundColor ?? defaults.backgroundColor,
+      elevation: useMaterial3
+        ? datePickerTheme.elevation ?? defaults.elevation!
+        : datePickerTheme.elevation ?? dialogTheme.elevation ?? 24,
+      shadowColor: datePickerTheme.shadowColor ?? defaults.shadowColor,
+      surfaceTintColor: datePickerTheme.surfaceTintColor ?? defaults.surfaceTintColor,
+      shape: useMaterial3
+        ? datePickerTheme.shape ?? defaults.shape
+        : datePickerTheme.shape ?? dialogTheme.shape ?? defaults.shape,
       insetPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0),
       clipBehavior: Clip.antiAlias,
       child: AnimatedContainer(
@@ -595,6 +622,7 @@
                   crossAxisAlignment: CrossAxisAlignment.stretch,
                   children: <Widget>[
                     header,
+                    if (useMaterial3) const Divider(),
                     Expanded(child: picker),
                     actions,
                   ],
@@ -605,6 +633,7 @@
                   crossAxisAlignment: CrossAxisAlignment.stretch,
                   children: <Widget>[
                     header,
+                    if (useMaterial3) const VerticalDivider(),
                     Flexible(
                       child: Column(
                         mainAxisSize: MainAxisSize.min,
@@ -736,17 +765,12 @@
 
   @override
   Widget build(BuildContext context) {
-    final ThemeData theme = Theme.of(context);
-    final ColorScheme colorScheme = theme.colorScheme;
-    final TextTheme textTheme = theme.textTheme;
-
-    // The header should use the primary color in light themes and surface color in dark
-    final bool isDark = colorScheme.brightness == Brightness.dark;
-    final Color primarySurfaceColor = isDark ? colorScheme.surface : colorScheme.primary;
-    final Color onPrimarySurfaceColor = isDark ? colorScheme.onSurface : colorScheme.onPrimary;
-
-    final TextStyle? helpStyle = textTheme.labelSmall?.copyWith(
-      color: onPrimarySurfaceColor,
+    final DatePickerThemeData themeData = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
+    final Color? backgroundColor = themeData.headerBackgroundColor ?? defaults.headerBackgroundColor;
+    final Color? foregroundColor = themeData.headerForegroundColor ?? defaults.headerForegroundColor;
+    final TextStyle? helpStyle = (themeData.headerHelpStyle ?? defaults.headerHelpStyle)?.copyWith(
+      color: foregroundColor,
     );
 
     final Text help = Text(
@@ -768,7 +792,7 @@
         return SizedBox(
           height: _datePickerHeaderPortraitHeight,
           child: Material(
-            color: primarySurfaceColor,
+            color: backgroundColor,
             child: Padding(
               padding: const EdgeInsetsDirectional.only(
                 start: 24,
@@ -796,7 +820,7 @@
         return SizedBox(
           width: _datePickerHeaderLandscapeWidth,
           child: Material(
-            color: primarySurfaceColor,
+            color: backgroundColor,
             child: Column(
               crossAxisAlignment: CrossAxisAlignment.start,
               children: <Widget>[
@@ -1292,18 +1316,20 @@
 
   @override
   Widget build(BuildContext context) {
+    final ThemeData theme = Theme.of(context);
+    final bool useMaterial3 = theme.useMaterial3;
     final Orientation orientation = MediaQuery.orientationOf(context);
     final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.3);
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
-    final ColorScheme colors = Theme.of(context).colorScheme;
-    final Color onPrimarySurface = colors.brightness == Brightness.light
-      ? colors.onPrimary
-      : colors.onSurface;
+    final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults =  DatePickerTheme.defaults(context);
 
     final Widget contents;
     final Size size;
-    ShapeBorder? shape;
-    final double elevation;
+    final double? elevation;
+    final Color? shadowColor;
+    final Color? surfaceTintColor;
+    final ShapeBorder? shape;
     final EdgeInsets insetPadding;
     final bool showEntryModeButton =
       _entryMode.value == DatePickerEntryMode.calendar ||
@@ -1324,28 +1350,29 @@
           onCancel: _handleCancel,
           entryModeButton: showEntryModeButton
             ? IconButton(
-                icon: const Icon(Icons.edit),
+                icon: Icon(useMaterial3 ? Icons.edit_outlined : Icons.edit),
                 padding: EdgeInsets.zero,
-                color: onPrimarySurface,
                 tooltip: localizations.inputDateModeButtonLabel,
                 onPressed: _handleEntryModeToggle,
               )
             : null,
           confirmText: widget.saveText ?? (
-            Theme.of(context).useMaterial3
+            useMaterial3
               ? localizations.saveButtonLabel
               : localizations.saveButtonLabel.toUpperCase()
           ),
           helpText: widget.helpText ?? (
-            Theme.of(context).useMaterial3
+            useMaterial3
               ? localizations.dateRangePickerHelpText
               : localizations.dateRangePickerHelpText.toUpperCase()
             ),
         );
         size = MediaQuery.sizeOf(context);
         insetPadding = EdgeInsets.zero;
-        shape = const RoundedRectangleBorder();
-        elevation = 0;
+        elevation = datePickerTheme.rangePickerElevation ?? defaults.rangePickerElevation!;
+        shadowColor = datePickerTheme.rangePickerShadowColor ?? defaults.rangePickerShadowColor!;
+        surfaceTintColor = datePickerTheme.rangePickerSurfaceTintColor ?? defaults.rangePickerSurfaceTintColor!;
+        shape = datePickerTheme.rangePickerShape ?? defaults.rangePickerShape;
         break;
 
       case DatePickerEntryMode.input:
@@ -1391,35 +1418,46 @@
             ? IconButton(
                 icon: const Icon(Icons.calendar_today),
                 padding: EdgeInsets.zero,
-                color: onPrimarySurface,
                 tooltip: localizations.calendarModeButtonLabel,
                 onPressed: _handleEntryModeToggle,
               )
             : null,
           confirmText: widget.confirmText ?? localizations.okButtonLabel,
           cancelText: widget.cancelText ?? (
-            Theme.of(context).useMaterial3
+            useMaterial3
               ? localizations.cancelButtonLabel
               : localizations.cancelButtonLabel.toUpperCase()
           ),
           helpText: widget.helpText ?? (
-            Theme.of(context).useMaterial3
+            useMaterial3
               ? localizations.dateRangePickerHelpText
               : localizations.dateRangePickerHelpText.toUpperCase()
           ),
         );
-        final DialogTheme dialogTheme = Theme.of(context).dialogTheme;
-        size = orientation == Orientation.portrait ? _inputPortraitDialogSize : _inputRangeLandscapeDialogSize;
+        final DialogTheme dialogTheme = theme.dialogTheme;
+        size = orientation == Orientation.portrait
+          ? (useMaterial3 ? _inputPortraitDialogSizeM3 : _inputPortraitDialogSizeM2)
+          : _inputRangeLandscapeDialogSize;
+        elevation = useMaterial3
+          ? datePickerTheme.elevation ?? defaults.elevation!
+          : datePickerTheme.elevation ?? dialogTheme.elevation ?? 24;
+        shadowColor = datePickerTheme.shadowColor ?? defaults.shadowColor;
+        surfaceTintColor = datePickerTheme.surfaceTintColor ?? defaults.surfaceTintColor;
+        shape = useMaterial3
+          ? datePickerTheme.shape ?? defaults.shape
+          : datePickerTheme.shape ?? dialogTheme.shape ?? defaults.shape;
+
         insetPadding = const EdgeInsets.symmetric(horizontal: 16.0, vertical: 24.0);
-        shape = dialogTheme.shape;
-        elevation = dialogTheme.elevation ?? 24;
         break;
     }
 
     return Dialog(
       insetPadding: insetPadding,
-      shape: shape,
+      backgroundColor: datePickerTheme.backgroundColor ?? defaults.backgroundColor,
       elevation: elevation,
+      shadowColor: shadowColor,
+      surfaceTintColor: surfaceTintColor,
+      shape: shape,
       clipBehavior: Clip.antiAlias,
       child: AnimatedContainer(
         width: size.width,
@@ -1472,26 +1510,29 @@
   @override
   Widget build(BuildContext context) {
     final ThemeData theme = Theme.of(context);
-    final ColorScheme colorScheme = theme.colorScheme;
+    final bool useMaterial3 = theme.useMaterial3;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
     final Orientation orientation = MediaQuery.orientationOf(context);
-    final TextTheme textTheme = theme.textTheme;
-    final Color headerForeground = colorScheme.brightness == Brightness.light
-        ? colorScheme.onPrimary
-        : colorScheme.onSurface;
-    final Color headerDisabledForeground = headerForeground.withOpacity(0.38);
+    final DatePickerThemeData themeData = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
+    final Color? dialogBackground = themeData.rangePickerBackgroundColor ?? defaults.rangePickerBackgroundColor;
+    final Color? headerForeground = themeData.rangePickerHeaderForegroundColor ?? defaults.rangePickerHeaderForegroundColor;
+    final Color? headerDisabledForeground = headerForeground?.withOpacity(0.38);
+    final TextStyle? headlineStyle = themeData.rangePickerHeaderHeadlineStyle ?? defaults.rangePickerHeaderHeadlineStyle;
+    final TextStyle? headlineHelpStyle = (themeData.rangePickerHeaderHelpStyle ?? defaults.rangePickerHeaderHelpStyle)?.apply(color: headerForeground);
     final String startDateText = _formatRangeStartDate(localizations, selectedStartDate, selectedEndDate);
     final String endDateText = _formatRangeEndDate(localizations, selectedStartDate, selectedEndDate, DateTime.now());
-    final TextStyle? headlineStyle = textTheme.headlineSmall;
     final TextStyle? startDateStyle = headlineStyle?.apply(
         color: selectedStartDate != null ? headerForeground : headerDisabledForeground,
     );
     final TextStyle? endDateStyle = headlineStyle?.apply(
         color: selectedEndDate != null ? headerForeground : headerDisabledForeground,
     );
-    final TextStyle saveButtonStyle = textTheme.labelLarge!.apply(
-        color: onConfirm != null ? headerForeground : headerDisabledForeground,
+    final ButtonStyle buttonStyle = TextButton.styleFrom(
+      foregroundColor: headerForeground,
+      disabledForegroundColor: headerDisabledForeground
     );
+    final IconThemeData iconTheme = IconThemeData(color: headerForeground);
 
     return SafeArea(
       top: false,
@@ -1499,6 +1540,11 @@
       right: false,
       child: Scaffold(
         appBar: AppBar(
+          iconTheme: iconTheme,
+          actionsIconTheme: iconTheme,
+          elevation: useMaterial3 ? 0 : null,
+          scrolledUnderElevation: useMaterial3 ? 0 : null,
+          backgroundColor: useMaterial3 ? Colors.transparent : null,
           leading: CloseButton(
             onPressed: onCancel,
           ),
@@ -1506,8 +1552,9 @@
             if (orientation == Orientation.landscape && entryModeButton != null)
               entryModeButton!,
             TextButton(
+              style: buttonStyle,
               onPressed: onConfirm,
-              child: Text(confirmText, style: saveButtonStyle),
+              child: Text(confirmText),
             ),
             const SizedBox(width: 8),
           ],
@@ -1522,12 +1569,7 @@
                   child: Column(
                     crossAxisAlignment: CrossAxisAlignment.start,
                     children: <Widget>[
-                      Text(
-                        helpText,
-                        style: textTheme.labelSmall!.apply(
-                          color: headerForeground,
-                        ),
-                      ),
+                      Text(helpText, style: headlineHelpStyle),
                       const SizedBox(height: 8),
                       Row(
                         children: <Widget>[
@@ -1557,11 +1599,15 @@
               if (orientation == Orientation.portrait && entryModeButton != null)
                 Padding(
                   padding: const EdgeInsets.symmetric(horizontal: 8.0),
-                  child: entryModeButton,
+                  child: IconTheme(
+                    data: iconTheme,
+                    child: entryModeButton!,
+                  ),
                 ),
             ]),
           ),
         ),
+        backgroundColor: dialogBackground,
         body: _CalendarDateRangePicker(
           initialStartDate: selectedStartDate,
           initialEndDate: selectedEndDate,
@@ -2201,7 +2247,8 @@
   }
 
   Color _highlightColor(BuildContext context) {
-    return Theme.of(context).colorScheme.primary.withOpacity(0.12);
+    return DatePickerTheme.of(context).rangeSelectionBackgroundColor
+      ?? DatePickerTheme.defaults(context).rangeSelectionBackgroundColor!;
   }
 
   void _dayFocusChanged(bool focused) {
@@ -2232,6 +2279,8 @@
     final ColorScheme colorScheme = theme.colorScheme;
     final TextTheme textTheme = theme.textTheme;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
+    final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
     final TextDirection textDirection = Directionality.of(context);
     final Color highlightColor = _highlightColor(context);
     final int day = dayToBuild.day;
@@ -2248,14 +2297,42 @@
       dayToBuild.isAfter(widget.selectedDateStart!) &&
       dayToBuild.isBefore(widget.selectedDateEnd!);
 
+    T? effectiveValue<T>(T? Function(DatePickerThemeData? theme) getProperty) {
+      return getProperty(datePickerTheme) ?? getProperty(defaults);
+    }
+
+    T? resolve<T>(MaterialStateProperty<T>? Function(DatePickerThemeData? theme) getProperty, Set<MaterialState> states) {
+      return effectiveValue(
+        (DatePickerThemeData? theme) {
+          return getProperty(theme)?.resolve(states);
+        },
+      );
+    }
+
+    final Set<MaterialState> states = <MaterialState>{
+      if (isDisabled) MaterialState.disabled,
+      if (isSelectedDayStart || isSelectedDayEnd) MaterialState.selected,
+    };
+
+    final Color? dayForegroundColor = resolve<Color?>((DatePickerThemeData? theme) => theme?.dayForegroundColor, states);
+    final Color? dayBackgroundColor = resolve<Color?>((DatePickerThemeData? theme) => theme?.dayBackgroundColor, states);
+    final MaterialStateProperty<Color?> dayOverlayColor = MaterialStateProperty.resolveWith<Color?>(
+      (Set<MaterialState> states) => effectiveValue(
+        (DatePickerThemeData? theme) =>
+          isInRange
+            ? theme?.rangeSelectionOverlayColor?.resolve(states)
+             : theme?.dayOverlayColor?.resolve(states),
+      )
+    );
+
     _HighlightPainter? highlightPainter;
 
     if (isSelectedDayStart || isSelectedDayEnd) {
       // The selected start and end dates gets a circle background
       // highlight, and a contrasting text color.
-      itemStyle = textTheme.bodyMedium?.apply(color: colorScheme.onPrimary);
+      itemStyle = textTheme.bodyMedium?.apply(color: dayForegroundColor);
       decoration = BoxDecoration(
-        color: colorScheme.primary,
+        color: dayBackgroundColor,
         shape: BoxShape.circle,
       );
 
@@ -2327,7 +2404,8 @@
         focusNode: _dayFocusNodes[day - 1],
         onTap: () => widget.onChanged(dayToBuild),
         radius: _monthItemRowHeight / 2 + 4,
-        splashColor: colorScheme.primary.withOpacity(0.38),
+        statesController: MaterialStatesController(states),
+        overlayColor: dayOverlayColor,
         onFocusChange: _dayFocusChanged,
         child: dayWidget,
       );
@@ -2350,8 +2428,7 @@
     final int daysInMonth = DateUtils.getDaysInMonth(year, month);
     final int dayOffset = DateUtils.firstDayOffset(year, month, localizations);
     final int weeks = ((daysInMonth + dayOffset) / DateTime.daysPerWeek).ceil();
-    final double gridHeight =
-        weeks * _monthItemRowHeight + (weeks - 1) * _monthItemSpaceBetweenRows;
+    final double gridHeight = weeks * _monthItemRowHeight + (weeks - 1) * _monthItemSpaceBetweenRows;
     final List<Widget> dayItems = <Widget>[];
 
     for (int i = 0; true; i += 1) {
@@ -2566,18 +2643,17 @@
 
   @override
   Widget build(BuildContext context) {
-    final ThemeData theme = Theme.of(context);
-    final ColorScheme colorScheme = theme.colorScheme;
+    final bool useMaterial3 = Theme.of(context).useMaterial3;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
     final Orientation orientation = MediaQuery.orientationOf(context);
-    final TextTheme textTheme = theme.textTheme;
+    final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context);
+    final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
 
-    final Color onPrimarySurfaceColor = colorScheme.brightness == Brightness.light
-        ? colorScheme.onPrimary
-        : colorScheme.onSurface;
-    final TextStyle? dateStyle = orientation == Orientation.landscape
-        ? textTheme.headlineSmall?.apply(color: onPrimarySurfaceColor)
-        : textTheme.headlineMedium?.apply(color: onPrimarySurfaceColor);
+    final Color? headerForegroundColor = datePickerTheme.headerForegroundColor ?? defaults.headerForegroundColor;
+    final TextStyle? headlineStyle = (datePickerTheme.headerHeadlineStyle ?? defaults.headerHeadlineStyle)?.copyWith(
+      color: headerForegroundColor,
+    );
+
     final String dateText = _formatDateRange(context, selectedStartDate, selectedEndDate, currentDate!);
     final String semanticDateText = selectedStartDate != null && selectedEndDate != null
         ? '${localizations.formatMediumDate(selectedStartDate!)} – ${localizations.formatMediumDate(selectedEndDate!)}'
@@ -2585,13 +2661,13 @@
 
     final Widget header = _DatePickerHeader(
       helpText: helpText ?? (
-        Theme.of(context).useMaterial3
+        useMaterial3
           ? localizations.dateRangePickerHelpText
           : localizations.dateRangePickerHelpText.toUpperCase()
       ),
       titleText: dateText,
       titleSemanticsLabel: semanticDateText,
-      titleStyle: dateStyle,
+      titleStyle: headlineStyle,
       orientation: orientation,
       isShort: orientation == Orientation.landscape,
       entryModeButton: entryModeButton,
@@ -2607,7 +2683,7 @@
           TextButton(
             onPressed: onCancel,
             child: Text(cancelText ?? (
-              theme.useMaterial3
+              useMaterial3
                 ? localizations.cancelButtonLabel
                 : localizations.cancelButtonLabel.toUpperCase()
             )),
@@ -2856,8 +2932,13 @@
 
   @override
   Widget build(BuildContext context) {
+    final ThemeData theme = Theme.of(context);
+    final bool useMaterial3 = theme.useMaterial3;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
-    final InputDecorationTheme inputTheme = Theme.of(context).inputDecorationTheme;
+    final InputDecorationTheme inputTheme = theme.inputDecorationTheme;
+    final InputBorder inputBorder = inputTheme.border
+      ?? (useMaterial3 ? const OutlineInputBorder() : const UnderlineInputBorder());
+
     return Row(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: <Widget>[
@@ -2865,7 +2946,7 @@
           child: TextField(
             controller: _startController,
             decoration: InputDecoration(
-              border: inputTheme.border ?? const UnderlineInputBorder(),
+              border: inputBorder,
               filled: inputTheme.filled,
               hintText: widget.fieldStartHintText ?? localizations.dateHelpText,
               labelText: widget.fieldStartLabelText ?? localizations.dateRangeStartLabel,
@@ -2881,7 +2962,7 @@
           child: TextField(
             controller: _endController,
             decoration: InputDecoration(
-              border: inputTheme.border ?? const UnderlineInputBorder(),
+              border: inputBorder,
               filled: inputTheme.filled,
               hintText: widget.fieldEndHintText ?? localizations.dateHelpText,
               labelText: widget.fieldEndLabelText ?? localizations.dateRangeEndLabel,
diff --git a/packages/flutter/lib/src/material/date_picker_theme.dart b/packages/flutter/lib/src/material/date_picker_theme.dart
new file mode 100644
index 0000000..0f6de36
--- /dev/null
+++ b/packages/flutter/lib/src/material/date_picker_theme.dart
@@ -0,0 +1,975 @@
+// 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 'dart:ui' show lerpDouble;
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+
+import 'color_scheme.dart';
+import 'colors.dart';
+import 'material_state.dart';
+import 'text_theme.dart';
+import 'theme.dart';
+
+// Examples can assume:
+// late BuildContext context;
+
+/// Overrides the default values of visual properties for descendant
+/// [DatePickerDialog] widgets.
+///
+/// Descendant widgets obtain the current [DatePickerThemeData] object with
+/// [DatePickerTheme.of]. Instances of [DatePickerTheme] can
+/// be customized with [DatePickerThemeData.copyWith].
+///
+/// Typically a [DatePickerTheme] is specified as part of the overall
+/// [Theme] with [ThemeData.datePickerTheme].
+///
+/// All [DatePickerThemeData] properties are null by default. When null,
+/// the [DatePickerDialog] computes its own default values, typically based on
+/// the overall theme's [ThemeData.colorScheme], [ThemeData.textTheme], and
+/// [ThemeData.iconTheme].
+@immutable
+class DatePickerThemeData with Diagnosticable {
+  /// Creates a [DatePickerThemeData] that can be used to override default properties
+  /// in a [DatePickerTheme] widget.
+  const DatePickerThemeData({
+    this.backgroundColor,
+    this.elevation,
+    this.shadowColor,
+    this.surfaceTintColor,
+    this.shape,
+    this.headerBackgroundColor,
+    this.headerForegroundColor,
+    this.headerHeadlineStyle,
+    this.headerHelpStyle,
+    this.weekdayStyle,
+    this.dayStyle,
+    this.dayForegroundColor,
+    this.dayBackgroundColor,
+    this.dayOverlayColor,
+    this.todayForegroundColor,
+    this.todayBackgroundColor,
+    this.todayBorder,
+    this.yearStyle,
+    this.yearForegroundColor,
+    this.yearBackgroundColor,
+    this.yearOverlayColor,
+    this.rangePickerBackgroundColor,
+    this.rangePickerElevation,
+    this.rangePickerShadowColor,
+    this.rangePickerSurfaceTintColor,
+    this.rangePickerShape,
+    this.rangePickerHeaderBackgroundColor,
+    this.rangePickerHeaderForegroundColor,
+    this.rangePickerHeaderHeadlineStyle,
+    this.rangePickerHeaderHelpStyle,
+    this.rangeSelectionBackgroundColor,
+    this.rangeSelectionOverlayColor,
+  });
+
+  /// Overrides the default value of [Dialog.backgroundColor].
+  final Color? backgroundColor;
+
+  /// Overrides the default value of [Dialog.elevation].
+  ///
+  /// See also:
+  ///   [Material.elevation], which explains how elevation is related to a component's shadow.
+  final double? elevation;
+
+  /// Overrides the default value of [Dialog.shadowColor].
+  ///
+  /// See also:
+  ///   [Material.shadowColor], which explains how the shadow is rendered.
+  final Color? shadowColor;
+
+  /// Overrides the default value of [Dialog.surfaceTintColor].
+  ///
+  /// See also:
+  ///   [Material.surfaceTintColor], which explains how this color is related to
+  ///   [elevation] and [backgroundColor].
+  final Color? surfaceTintColor;
+
+  /// Overrides the default value of [Dialog.shape].
+  ///
+  /// If [elevation] is greater than zero then a shadow is shown and the shadow's
+  /// shape mirrors the shape of the dialog.
+  final ShapeBorder? shape;
+
+  /// Overrides the header's default background fill color.
+  ///
+  /// The dialog's header displays the currently selected date.
+  final Color? headerBackgroundColor;
+
+  /// Overrides the header's default color used for text labels and icons.
+  ///
+  /// The dialog's header displays the currently selected date.
+  ///
+  /// This is used instead of the [TextStyle.color] property of [headerHeadlineStyle]
+  /// and [headerHelpStyle].
+  final Color? headerForegroundColor;
+
+  /// Overrides the header's default headline text style.
+  ///
+  /// The dialog's header displays the currently selected date.
+  ///
+  /// The [TextStyle.color] of the [headerHeadlineStyle] is not used,
+  /// [headerForegroundColor] is used instead.
+  final TextStyle? headerHeadlineStyle;
+
+  /// Overrides the header's default help text style.
+  ///
+  /// The help text (also referred to as "supporting text" in the Material
+  /// spec) is usually a prompt to the user at the top of the header
+  /// (i.e. 'Select date').
+  ///
+  /// The [TextStyle.color] of the [headerHelpStyle] is not used,
+  /// [headerForegroundColor] is used instead.
+  ///
+  /// See also:
+  ///   [DatePickerDialog.helpText], which specifies the help text.
+  final TextStyle? headerHelpStyle;
+
+  /// Overrides the default text style used for the row of weekday
+  /// labels at the top of the date picker grid.
+  final TextStyle? weekdayStyle;
+
+  /// Overrides the default text style used for each individual day
+  /// label in the grid of the date picker.
+  ///
+  /// The [TextStyle.color] of the [dayStyle] is not used,
+  /// [dayForegroundColor] is used instead.
+  final TextStyle? dayStyle;
+
+  /// Overrides the default color used to paint the day labels in the
+  /// grid of the date picker.
+  ///
+  /// This will be used instead of the color provided in [dayStyle].
+  final MaterialStateProperty<Color?>? dayForegroundColor;
+
+  /// Overrides the default color used to paint the background of the
+  /// day labels in the grid of the date picker.
+  final MaterialStateProperty<Color?>? dayBackgroundColor;
+
+  /// Overriddes the default highlight color that's typically used to
+  /// indicate that a day in the grid is focused, hovered, or pressed.
+  final MaterialStateProperty<Color?>? dayOverlayColor;
+
+  /// Overrides the default color used to paint the
+  /// [DatePickerDialog.currentDate] label in the grid of the dialog's
+  /// [CalendarDatePicker] and the corresponding year in the dialog's
+  /// [YearPicker].
+  ///
+  /// This will be used instead of the [TextStyle.color] provided in [dayStyle].
+  final MaterialStateProperty<Color?>? todayForegroundColor;
+
+  /// Overrides the default color used to paint the background of the
+  /// [DatePickerDialog.currentDate] label in the grid of the date picker.
+  final MaterialStateProperty<Color?>? todayBackgroundColor;
+
+  /// Overrides the border used to paint the
+  /// [DatePickerDialog.currentDate] label in the grid of the date
+  /// picker.
+  ///
+  /// The border side's [BorderSide.color] is not used,
+  /// [todayForegroundColor] is used instead.
+  final BorderSide? todayBorder;
+
+  /// Overrides the default text style used to paint each of the year
+  /// entries in the year selector of the date picker.
+  ///
+  /// The [TextStyle.color] of the [yearStyle] is not used,
+  /// [yearForegroundColor] is used instead.
+  final TextStyle? yearStyle;
+
+  /// Overrides the default color used to paint the year labels in the year
+  /// selector of the date picker.
+  ///
+  /// This will be used instead of the color provided in [yearStyle].
+  final MaterialStateProperty<Color?>? yearForegroundColor;
+
+  /// Overrides the default color used to paint the background of the
+  /// year labels in the year selector of the of the date picker.
+  final MaterialStateProperty<Color?>? yearBackgroundColor;
+
+  /// Overrides the default highlight color that's typically used to
+  /// indicate that a year in the year selector is focused, hovered,
+  /// or pressed.
+  final MaterialStateProperty<Color?>? yearOverlayColor;
+
+  /// Overrides the default [Scaffold.backgroundColor] for
+  /// [DateRangePickerDialog].
+  final Color? rangePickerBackgroundColor;
+
+  /// Overrides the default elevation of the full screen
+  /// [DateRangePickerDialog].
+  ///
+  /// See also:
+  ///   [Material.elevation], which explains how elevation is related to a component's shadow.
+  final double? rangePickerElevation;
+
+  /// Overrides the color of the shadow painted below a full screen
+  /// [DateRangePickerDialog].
+  ///
+  /// See also:
+  ///   [Material.shadowColor], which explains how the shadow is rendered.
+  final Color? rangePickerShadowColor;
+
+  /// Overrides the default color of the surface tint overlay applied
+  /// to the [backgroundColor] of a full screen
+  /// [DateRangePickerDialog]'s to indicate elevation.
+  ///
+  /// See also:
+  ///   [Material.surfaceTintColor], which explains how this color is related to
+  ///   [elevation].
+  final Color? rangePickerSurfaceTintColor;
+
+  /// Overrides the default overall shape of a full screen
+  /// [DateRangePickerDialog].
+  ///
+  /// If [elevation] is greater than zero then a shadow is shown and the shadow's
+  /// shape mirrors the shape of the dialog.
+  ///
+  ///   [Material.surfaceTintColor], which explains how this color is related to
+  ///   [elevation].
+  final ShapeBorder? rangePickerShape;
+
+  /// Overrides the default background fill color for [DateRangePickerDialog].
+  ///
+  /// The dialog's header displays the currently selected date range.
+  final Color? rangePickerHeaderBackgroundColor;
+
+  /// Overrides the default color used for text labels and icons in
+  /// the header of a full screen [DateRangePickerDialog]
+  ///
+  /// The dialog's header displays the currently selected date range.
+  ///
+  /// This is used instead of any colors provided by
+  /// [rangePickerHeaderHeadlineStyle] or [rangePickerHeaderHelpStyle].
+  final Color? rangePickerHeaderForegroundColor;
+
+  /// Overrides the default text style used for the headline text in
+  /// the header of a full screen [DateRangePickerDialog].
+  ///
+  /// The dialog's header displays the currently selected date range.
+  ///
+  /// The [TextStyle.color] of [rangePickerHeaderHeadlineStyle] is not used,
+  /// [rangePickerHeaderForegroundColor] is used instead.
+  final TextStyle? rangePickerHeaderHeadlineStyle;
+
+  /// Overrides the default text style used for the help text of the
+  /// header of a full screen [DateRangePickerDialog].
+  ///
+  /// The help text (also referred to as "supporting text" in the Material
+  /// spec) is usually a prompt to the user at the top of the header
+  /// (i.e. 'Select date').
+  ///
+  /// The [TextStyle.color] of the [rangePickerHeaderHelpStyle] is not used,
+  /// [rangePickerHeaderForegroundColor] is used instead.
+  ///
+  /// See also:
+  ///   [DateRangePickerDialog.helpText], which specifies the help text.
+  final TextStyle? rangePickerHeaderHelpStyle;
+
+  /// Overrides the default background color used to paint days
+  /// selected between the start and end dates in a
+  /// [DateRangePickerDialog].
+  final Color? rangeSelectionBackgroundColor;
+
+  /// Overrides the default highlight color that's typically used to
+  /// indicate that a date in the selected range of a
+  /// [DateRangePickerDialog] is focused, hovered, or pressed.
+  final MaterialStateProperty<Color?>? rangeSelectionOverlayColor;
+
+  /// Creates a copy of this object with the given fields replaced with the
+  /// new values.
+  DatePickerThemeData copyWith({
+    Color? backgroundColor,
+    double? elevation,
+    Color? shadowColor,
+    Color? surfaceTintColor,
+    ShapeBorder? shape,
+    Color? headerBackgroundColor,
+    Color? headerForegroundColor,
+    TextStyle? headerHeadlineStyle,
+    TextStyle? headerHelpStyle,
+    TextStyle? weekdayStyle,
+    TextStyle? dayStyle,
+    MaterialStateProperty<Color?>? dayForegroundColor,
+    MaterialStateProperty<Color?>? dayBackgroundColor,
+    MaterialStateProperty<Color?>? dayOverlayColor,
+    MaterialStateProperty<Color?>? todayForegroundColor,
+    MaterialStateProperty<Color?>? todayBackgroundColor,
+    BorderSide? todayBorder,
+    TextStyle? yearStyle,
+    MaterialStateProperty<Color?>? yearForegroundColor,
+    MaterialStateProperty<Color?>? yearBackgroundColor,
+    MaterialStateProperty<Color?>? yearOverlayColor,
+    Color? rangePickerBackgroundColor,
+    double? rangePickerElevation,
+    Color? rangePickerShadowColor,
+    Color? rangePickerSurfaceTintColor,
+    ShapeBorder? rangePickerShape,
+    Color? rangePickerHeaderBackgroundColor,
+    Color? rangePickerHeaderForegroundColor,
+    TextStyle? rangePickerHeaderHeadlineStyle,
+    TextStyle? rangePickerHeaderHelpStyle,
+    Color? rangeSelectionBackgroundColor,
+    MaterialStateProperty<Color?>? rangeSelectionOverlayColor,
+  }) {
+    return DatePickerThemeData(
+      backgroundColor: backgroundColor ?? this.backgroundColor,
+      elevation: elevation ?? this.elevation,
+      shadowColor: shadowColor ?? this.shadowColor,
+      surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
+      shape: shape ?? this.shape,
+      headerBackgroundColor: headerBackgroundColor ?? this.headerBackgroundColor,
+      headerForegroundColor: headerForegroundColor ?? this.headerForegroundColor,
+      headerHeadlineStyle: headerHeadlineStyle ?? this.headerHeadlineStyle,
+      headerHelpStyle: headerHelpStyle ?? this.headerHelpStyle,
+      weekdayStyle: weekdayStyle ?? this.weekdayStyle,
+      dayStyle: dayStyle ?? this.dayStyle,
+      dayForegroundColor: dayForegroundColor ?? this.dayForegroundColor,
+      dayBackgroundColor: dayBackgroundColor ?? this.dayBackgroundColor,
+      dayOverlayColor: dayOverlayColor ?? this.dayOverlayColor,
+      todayForegroundColor: todayForegroundColor ?? this.todayForegroundColor,
+      todayBackgroundColor: todayBackgroundColor ?? this.todayBackgroundColor,
+      todayBorder: todayBorder ?? this.todayBorder,
+      yearStyle: yearStyle ?? this.yearStyle,
+      yearForegroundColor: yearForegroundColor ?? this.yearForegroundColor,
+      yearBackgroundColor: yearBackgroundColor ?? this.yearBackgroundColor,
+      yearOverlayColor: yearOverlayColor ?? this.yearOverlayColor,
+      rangePickerBackgroundColor: rangePickerBackgroundColor ?? this.rangePickerBackgroundColor,
+      rangePickerElevation: rangePickerElevation ?? this.rangePickerElevation,
+      rangePickerShadowColor: rangePickerShadowColor ?? this.rangePickerShadowColor,
+      rangePickerSurfaceTintColor: rangePickerSurfaceTintColor ?? this.rangePickerSurfaceTintColor,
+      rangePickerShape: rangePickerShape ?? this.rangePickerShape,
+      rangePickerHeaderBackgroundColor: rangePickerHeaderBackgroundColor ?? this.rangePickerHeaderBackgroundColor,
+      rangePickerHeaderForegroundColor: rangePickerHeaderForegroundColor ?? this.rangePickerHeaderForegroundColor,
+      rangePickerHeaderHeadlineStyle: rangePickerHeaderHeadlineStyle ?? this.rangePickerHeaderHeadlineStyle,
+      rangePickerHeaderHelpStyle: rangePickerHeaderHelpStyle ?? this.rangePickerHeaderHelpStyle,
+      rangeSelectionBackgroundColor: rangeSelectionBackgroundColor ?? this.rangeSelectionBackgroundColor,
+      rangeSelectionOverlayColor: rangeSelectionOverlayColor ?? this.rangeSelectionOverlayColor,
+    );
+  }
+
+  /// Linearly interpolates between two [DatePickerThemeData].
+  static DatePickerThemeData lerp(DatePickerThemeData? a, DatePickerThemeData? b, double t) {
+    return DatePickerThemeData(
+      backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
+      elevation: lerpDouble(a?.elevation, b?.elevation, t),
+      shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
+      surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
+      shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
+      headerBackgroundColor: Color.lerp(a?.headerBackgroundColor, b?.headerBackgroundColor, t),
+      headerForegroundColor: Color.lerp(a?.headerForegroundColor, b?.headerForegroundColor, t),
+      headerHeadlineStyle: TextStyle.lerp(a?.headerHeadlineStyle, b?.headerHeadlineStyle, t),
+      headerHelpStyle: TextStyle.lerp(a?.headerHelpStyle, b?.headerHelpStyle, t),
+      weekdayStyle: TextStyle.lerp(a?.weekdayStyle, b?.weekdayStyle, t),
+      dayStyle: TextStyle.lerp(a?.dayStyle, b?.dayStyle, t),
+      dayForegroundColor: MaterialStateProperty.lerp<Color?>(a?.dayForegroundColor, b?.dayForegroundColor, t, Color.lerp),
+      dayBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.dayBackgroundColor, b?.dayBackgroundColor, t, Color.lerp),
+      dayOverlayColor: MaterialStateProperty.lerp<Color?>(a?.dayOverlayColor, b?.dayOverlayColor, t, Color.lerp),
+      todayForegroundColor: MaterialStateProperty.lerp<Color?>(a?.todayForegroundColor, b?.todayForegroundColor, t, Color.lerp),
+      todayBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.todayBackgroundColor, b?.todayBackgroundColor, t, Color.lerp),
+      todayBorder: _lerpBorderSide(a?.todayBorder, b?.todayBorder, t),
+      yearStyle: TextStyle.lerp(a?.yearStyle, b?.yearStyle, t),
+      yearForegroundColor: MaterialStateProperty.lerp<Color?>(a?.yearForegroundColor, b?.yearForegroundColor, t, Color.lerp),
+      yearBackgroundColor: MaterialStateProperty.lerp<Color?>(a?.yearBackgroundColor, b?.yearBackgroundColor, t, Color.lerp),
+      yearOverlayColor: MaterialStateProperty.lerp<Color?>(a?.yearOverlayColor, b?.yearOverlayColor, t, Color.lerp),
+      rangePickerBackgroundColor: Color.lerp(a?.rangePickerBackgroundColor, b?.rangePickerBackgroundColor, t),
+      rangePickerElevation: lerpDouble(a?.rangePickerElevation, b?.rangePickerElevation, t),
+      rangePickerShadowColor: Color.lerp(a?.rangePickerShadowColor, b?.rangePickerShadowColor, t),
+      rangePickerSurfaceTintColor: Color.lerp(a?.rangePickerSurfaceTintColor, b?.rangePickerSurfaceTintColor, t),
+      rangePickerShape: ShapeBorder.lerp(a?.rangePickerShape, b?.rangePickerShape, t),
+      rangePickerHeaderBackgroundColor: Color.lerp(a?.rangePickerHeaderBackgroundColor, b?.rangePickerHeaderBackgroundColor, t),
+      rangePickerHeaderForegroundColor: Color.lerp(a?.rangePickerHeaderForegroundColor, b?.rangePickerHeaderForegroundColor, t),
+      rangePickerHeaderHeadlineStyle: TextStyle.lerp(a?.rangePickerHeaderHeadlineStyle, b?.rangePickerHeaderHeadlineStyle, t),
+      rangePickerHeaderHelpStyle: TextStyle.lerp(a?.rangePickerHeaderHelpStyle, b?.rangePickerHeaderHelpStyle, t),
+      rangeSelectionBackgroundColor: Color.lerp(a?.rangeSelectionBackgroundColor, b?.rangeSelectionBackgroundColor, t),
+      rangeSelectionOverlayColor: MaterialStateProperty.lerp<Color?>(a?.rangeSelectionOverlayColor, b?.rangeSelectionOverlayColor, t, Color.lerp),
+    );
+  }
+
+  static BorderSide? _lerpBorderSide(BorderSide? a, BorderSide? b, double t) {
+    if (a == null && b == null) {
+      return null;
+    }
+    if (a == null) {
+      return BorderSide.lerp(BorderSide(width: 0, color: b!.color.withAlpha(0)), b, t);
+    }
+    return BorderSide.lerp(a, BorderSide(width: 0, color: a.color.withAlpha(0)), t);
+  }
+
+  @override
+  int get hashCode => Object.hashAll(<Object?>[
+    backgroundColor,
+    elevation,
+    shadowColor,
+    surfaceTintColor,
+    shape,
+    headerBackgroundColor,
+    headerForegroundColor,
+    headerHeadlineStyle,
+    headerHelpStyle,
+    weekdayStyle,
+    dayStyle,
+    dayForegroundColor,
+    dayBackgroundColor,
+    dayOverlayColor,
+    todayForegroundColor,
+    todayBackgroundColor,
+    todayBorder,
+    yearStyle,
+    yearForegroundColor,
+    yearBackgroundColor,
+    yearOverlayColor,
+    rangePickerBackgroundColor,
+    rangePickerElevation,
+    rangePickerShadowColor,
+    rangePickerSurfaceTintColor,
+    rangePickerShape,
+    rangePickerHeaderBackgroundColor,
+    rangePickerHeaderForegroundColor,
+    rangePickerHeaderHeadlineStyle,
+    rangePickerHeaderHelpStyle,
+    rangeSelectionBackgroundColor,
+    rangeSelectionOverlayColor,
+  ]);
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) {
+      return true;
+    }
+    return other is DatePickerThemeData
+      && other.backgroundColor == backgroundColor
+      && other.elevation == elevation
+      && other.shadowColor == shadowColor
+      && other.surfaceTintColor == surfaceTintColor
+      && other.shape == shape
+      && other.headerBackgroundColor == headerBackgroundColor
+      && other.headerForegroundColor == headerForegroundColor
+      && other.headerHeadlineStyle == headerHeadlineStyle
+      && other.headerHelpStyle == headerHelpStyle
+      && other.weekdayStyle == weekdayStyle
+      && other.dayStyle == dayStyle
+      && other.dayForegroundColor == dayForegroundColor
+      && other.dayBackgroundColor == dayBackgroundColor
+      && other.dayOverlayColor == dayOverlayColor
+      && other.todayForegroundColor == todayForegroundColor
+      && other.todayBackgroundColor == todayBackgroundColor
+      && other.todayBorder == todayBorder
+      && other.yearStyle == yearStyle
+      && other.yearForegroundColor == yearForegroundColor
+      && other.yearBackgroundColor == yearBackgroundColor
+      && other.yearOverlayColor == yearOverlayColor
+      && other.rangePickerBackgroundColor == rangePickerBackgroundColor
+      && other.rangePickerElevation == rangePickerElevation
+      && other.rangePickerShadowColor == rangePickerShadowColor
+      && other.rangePickerSurfaceTintColor == rangePickerSurfaceTintColor
+      && other.rangePickerShape == rangePickerShape
+      && other.rangePickerHeaderBackgroundColor == rangePickerHeaderBackgroundColor
+      && other.rangePickerHeaderForegroundColor == rangePickerHeaderForegroundColor
+      && other.rangePickerHeaderHeadlineStyle == rangePickerHeaderHeadlineStyle
+      && other.rangePickerHeaderHelpStyle == rangePickerHeaderHelpStyle
+      && other.rangeSelectionBackgroundColor == rangeSelectionBackgroundColor
+      && other.rangeSelectionOverlayColor == rangeSelectionOverlayColor;
+  }
+
+  @override
+  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+    super.debugFillProperties(properties);
+    properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
+    properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
+    properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
+    properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
+    properties.add(ColorProperty('headerBackgroundColor', headerBackgroundColor, defaultValue: null));
+    properties.add(ColorProperty('headerForegroundColor', headerForegroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('headerHeadlineStyle', headerHeadlineStyle, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('headerHelpStyle', headerHelpStyle, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('weekDayStyle', weekdayStyle, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('dayStyle', dayStyle, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayForegroundColor', dayForegroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayBackgroundColor', dayBackgroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('dayOverlayColor', dayOverlayColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('todayForegroundColor', todayForegroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('todayBackgroundColor', todayBackgroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<BorderSide?>('todayBorder', todayBorder, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('yearStyle', yearStyle, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('yearForegroundColor', yearForegroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('yearBackgroundColor', yearBackgroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('yearOverlayColor', yearOverlayColor, defaultValue: null));
+    properties.add(ColorProperty('rangePickerBackgroundColor', rangePickerBackgroundColor, defaultValue: null));
+    properties.add(DoubleProperty('rangePickerElevation', rangePickerElevation, defaultValue: null));
+    properties.add(ColorProperty('rangePickerShadowColor', rangePickerShadowColor, defaultValue: null));
+    properties.add(ColorProperty('rangePickerSurfaceTintColor', rangePickerSurfaceTintColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<ShapeBorder>('rangePickerShape', rangePickerShape, defaultValue: null));
+    properties.add(ColorProperty('rangePickerHeaderBackgroundColor', rangePickerHeaderBackgroundColor, defaultValue: null));
+    properties.add(ColorProperty('rangePickerHeaderForegroundColor', rangePickerHeaderForegroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('rangePickerHeaderHeadlineStyle', rangePickerHeaderHeadlineStyle, defaultValue: null));
+    properties.add(DiagnosticsProperty<TextStyle>('rangePickerHeaderHelpStyle', rangePickerHeaderHelpStyle, defaultValue: null));
+    properties.add(ColorProperty('rangeSelectionBackgroundColor', rangeSelectionBackgroundColor, defaultValue: null));
+    properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('rangeSelectionOverlayColor', rangeSelectionOverlayColor, defaultValue: null));
+  }
+}
+
+/// An inherited widget that defines the visual properties for
+/// [DatePickerDialog]s in this widget's subtree.
+///
+/// Values specified here are used for [DatePickerDialog] properties that are not
+/// given an explicit non-null value.
+class DatePickerTheme extends InheritedTheme {
+  /// Creates a [DatePickerTheme] that controls visual parameters for
+  /// descendent [DatePickerDialog]s.
+  const DatePickerTheme({
+    super.key,
+    required this.data,
+    required super.child,
+  });
+
+  /// Specifies the visual properties used by descendant [DatePickerDialog]
+  /// widgets.
+  final DatePickerThemeData data;
+
+  /// The [data] from the closest instance of this class that encloses the given
+  /// context.
+  ///
+  /// If there is no [DatePickerTheme] in scope, this will return
+  /// [ThemeData.datePickerTheme] from the ambient [Theme].
+  ///
+  /// Typical usage is as follows:
+  ///
+  /// ```dart
+  /// DatePickerThemeData theme = DatePickerTheme.of(context);
+  /// ```
+  ///
+  /// See also:
+  ///
+  ///  * [maybeOf], which returns null if it doesn't find a
+  ///    [DatePickerTheme] ancestor.
+  ///  * [defaults], which will return the default properties used when no
+  ///    other [DatePickerTheme] has been provided.
+  static DatePickerThemeData of(BuildContext context) {
+    return maybeOf(context) ?? Theme.of(context).datePickerTheme;
+  }
+
+  /// The data from the closest instance of this class that encloses the given
+  /// context, if any.
+  ///
+  /// Use this function if you want to allow situations where no
+  /// [DatePickerTheme] is in scope. Prefer using [DatePickerTheme.of]
+  /// in situations where a [DatePickerThemeData] is expected to be
+  /// non-null.
+  ///
+  /// If there is no [DatePickerTheme] in scope, then this function will
+  /// return null.
+  ///
+  /// Typical usage is as follows:
+  ///
+  /// ```dart
+  /// DatePickerThemeData? theme = DatePickerTheme.maybeOf(context);
+  /// if (theme == null) {
+  ///   // Do something else instead.
+  /// }
+  /// ```
+  ///
+  /// See also:
+  ///
+  ///  * [of], which will return [ThemeData.datePickerTheme] if it doesn't
+  ///    find a [DatePickerTheme] ancestor, instead of returning null.
+  ///  * [defaults], which will return the default properties used when no
+  ///    other [DatePickerTheme] has been provided.
+  static DatePickerThemeData? maybeOf(BuildContext context) {
+    return context.dependOnInheritedWidgetOfExactType<DatePickerTheme>()?.data;
+  }
+
+  /// A DatePickerThemeData used as the default properties for date pickers.
+  ///
+  /// This is only used for properties not already specified in the ambient
+  /// [DatePickerTheme.of].
+  ///
+  /// See also:
+  ///
+  ///  * [of], which will return [ThemeData.datePickerTheme] if it doesn't
+  ///    find a [DatePickerTheme] ancestor, instead of returning null.
+  ///  * [maybeOf], which returns null if it doesn't find a
+  ///    [DatePickerTheme] ancestor.
+  static DatePickerThemeData defaults(BuildContext context) {
+    return Theme.of(context).useMaterial3
+      ? _DatePickerDefaultsM3(context)
+      : _DatePickerDefaultsM2(context);
+  }
+
+  @override
+  Widget wrap(BuildContext context, Widget child) {
+    return DatePickerTheme(data: data, child: child);
+  }
+
+  @override
+  bool updateShouldNotify(DatePickerTheme oldWidget) => data != oldWidget.data;
+}
+
+// Hand coded defaults based on Material Design 2.
+class _DatePickerDefaultsM2 extends DatePickerThemeData {
+  _DatePickerDefaultsM2(this.context)
+    : super(
+        elevation: 24.0,
+        shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))),
+        rangePickerElevation: 0.0,
+        rangePickerShape: const RoundedRectangleBorder(),
+      );
+
+  final BuildContext context;
+  late final ThemeData _theme = Theme.of(context);
+  late final ColorScheme _colors = _theme.colorScheme;
+  late final TextTheme _textTheme = _theme.textTheme;
+  late final bool _isDark = _colors.brightness == Brightness.dark;
+
+  @override
+  Color? get headerBackgroundColor => _isDark ? _colors.surface : _colors.primary;
+
+  @override
+  Color? get headerForegroundColor => _isDark ? _colors.onSurface : _colors.onPrimary;
+
+  @override
+  TextStyle? get headerHeadlineStyle => _textTheme.headlineSmall;
+
+  @override
+  TextStyle? get headerHelpStyle => _textTheme.labelSmall;
+
+  @override
+  TextStyle? get weekdayStyle => _textTheme.bodySmall?.apply(
+    color: _colors.onSurface.withOpacity(0.60),
+  );
+
+  @override
+  TextStyle? get dayStyle => _textTheme.bodySmall;
+
+  @override
+  MaterialStateProperty<Color?>? get dayForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.onPrimary;
+      } else if (states.contains(MaterialState.disabled)) {
+        return _colors.onSurface.withOpacity(0.38);
+      }
+      return _colors.onSurface;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get dayBackgroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.primary;
+      }
+      return null;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get dayOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onPrimary.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onPrimary.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onPrimary.withOpacity(0.38);
+        }
+      } else {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onSurfaceVariant.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+      }
+      return null;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get todayForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.onPrimary;
+      } else if (states.contains(MaterialState.disabled)) {
+        return _colors.onSurface.withOpacity(0.38);
+      }
+      return _colors.primary;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get todayBackgroundColor => dayBackgroundColor;
+
+  @override
+  BorderSide? get todayBorder => BorderSide(color: _colors.primary);
+
+  @override
+  TextStyle? get yearStyle => _textTheme.bodyLarge;
+
+  @override
+  Color? get rangePickerBackgroundColor => _colors.surface;
+
+  @override
+  Color? get rangePickerShadowColor => Colors.transparent;
+
+  @override
+  Color? get rangePickerSurfaceTintColor => Colors.transparent;
+
+  @override
+  Color? get rangePickerHeaderBackgroundColor => _isDark ? _colors.surface : _colors.primary;
+
+  @override
+  Color? get rangePickerHeaderForegroundColor => _isDark ? _colors.onSurface : _colors.onPrimary;
+
+  @override
+  TextStyle? get rangePickerHeaderHeadlineStyle => _textTheme.headlineSmall;
+
+  @override
+  TextStyle? get rangePickerHeaderHelpStyle => _textTheme.labelSmall;
+
+  @override
+  Color? get rangeSelectionBackgroundColor => _colors.primary.withOpacity(0.12);
+
+  @override
+  MaterialStateProperty<Color?>? get rangeSelectionOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onPrimary.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onPrimary.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onPrimary.withOpacity(0.38);
+        }
+      } else {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onSurfaceVariant.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+      }
+      return null;
+    });
+}
+
+// BEGIN GENERATED TOKEN PROPERTIES - DatePicker
+
+// Do not edit by hand. The code between the "BEGIN GENERATED" and
+// "END GENERATED" comments are generated from data in the Material
+// Design token database by the script:
+//   dev/tools/gen_defaults/bin/gen_defaults.dart.
+
+// Token database version: v0_150
+
+class _DatePickerDefaultsM3 extends DatePickerThemeData {
+  _DatePickerDefaultsM3(this.context)
+    : super(
+        elevation: 6.0,
+        shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0))),
+        rangePickerElevation: 0.0,
+        rangePickerShape: const RoundedRectangleBorder(),
+      );
+
+  final BuildContext context;
+  late final ThemeData _theme = Theme.of(context);
+  late final ColorScheme _colors = _theme.colorScheme;
+  late final TextTheme _textTheme = _theme.textTheme;
+
+  @override
+  Color? get backgroundColor => _colors.surface;
+
+  @override
+  Color? get shadowColor => Colors.transparent;
+
+  @override
+  Color? get surfaceTintColor => _colors.surfaceTint;
+
+  @override
+  Color? get headerBackgroundColor => Colors.transparent;
+
+  @override
+  Color? get headerForegroundColor => _colors.onSurfaceVariant;
+
+  @override
+  TextStyle? get headerHeadlineStyle => _textTheme.headlineLarge;
+
+  @override
+  TextStyle? get headerHelpStyle => _textTheme.labelMedium;
+
+  @override
+  TextStyle? get weekdayStyle => _textTheme.bodySmall?.apply(
+    color: _colors.onSurface,
+  );
+
+  @override
+  TextStyle? get dayStyle => _textTheme.bodySmall;
+
+  @override
+  MaterialStateProperty<Color?>? get dayForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.onPrimary;
+      } else if (states.contains(MaterialState.disabled)) {
+        return _colors.onSurface.withOpacity(0.38);
+      }
+      return _colors.onSurface;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get dayBackgroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.primary;
+      }
+      return null;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get dayOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onPrimary.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onPrimary.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onPrimary.withOpacity(0.12);
+        }
+      } else {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onSurfaceVariant.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+      }
+      return null;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get todayForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.onPrimary;
+      } else if (states.contains(MaterialState.disabled)) {
+        return _colors.primary.withOpacity(0.38);
+      }
+      return _colors.primary;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get todayBackgroundColor => dayBackgroundColor;
+
+  @override
+  BorderSide? get todayBorder => BorderSide(color: _colors.primary);
+
+  @override
+  TextStyle? get yearStyle => _textTheme.bodyLarge;
+
+  @override
+  MaterialStateProperty<Color?>? get yearForegroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.onPrimary;
+      } else if (states.contains(MaterialState.disabled)) {
+        return _colors.onSurfaceVariant.withOpacity(0.38);
+      }
+      return _colors.onSurfaceVariant;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get yearBackgroundColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        return _colors.primary;
+      }
+      return null;
+    });
+
+  @override
+  MaterialStateProperty<Color?>? get yearOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.selected)) {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onPrimary.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onPrimary.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onPrimary.withOpacity(0.12);
+        }
+      } else {
+        if (states.contains(MaterialState.hovered)) {
+          return _colors.onSurfaceVariant.withOpacity(0.08);
+        }
+        if (states.contains(MaterialState.focused)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+        if (states.contains(MaterialState.pressed)) {
+          return _colors.onSurfaceVariant.withOpacity(0.12);
+        }
+      }
+      return null;
+    });
+
+    @override
+    Color? get rangePickerShadowColor => Colors.transparent;
+
+    @override
+    Color? get rangePickerSurfaceTintColor => Colors.transparent;
+
+    @override
+    Color? get rangeSelectionBackgroundColor => _colors.primaryContainer;
+
+  @override
+  MaterialStateProperty<Color?>? get rangeSelectionOverlayColor =>
+    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
+      if (states.contains(MaterialState.hovered)) {
+        return null;
+      }
+      if (states.contains(MaterialState.focused)) {
+        return null;
+      }
+      if (states.contains(MaterialState.pressed)) {
+        return null;
+      }
+      return null;
+    });
+
+  @override
+  Color? get rangePickerHeaderBackgroundColor => Colors.transparent;
+
+  @override
+  Color? get rangePickerHeaderForegroundColor => _colors.onSurfaceVariant;
+
+  @override
+  TextStyle? get rangePickerHeaderHeadlineStyle => _textTheme.titleLarge;
+
+  @override
+  TextStyle? get rangePickerHeaderHelpStyle => _textTheme.titleSmall;
+
+
+}
+
+// END GENERATED TOKEN PROPERTIES - DatePicker
diff --git a/packages/flutter/lib/src/material/input_date_picker_form_field.dart b/packages/flutter/lib/src/material/input_date_picker_form_field.dart
index 9cb7615..7c68804 100644
--- a/packages/flutter/lib/src/material/input_date_picker_form_field.dart
+++ b/packages/flutter/lib/src/material/input_date_picker_form_field.dart
@@ -234,11 +234,16 @@
 
   @override
   Widget build(BuildContext context) {
+    final ThemeData theme = Theme.of(context);
+    final bool useMaterial3 = theme.useMaterial3;
     final MaterialLocalizations localizations = MaterialLocalizations.of(context);
-    final InputDecorationTheme inputTheme = Theme.of(context).inputDecorationTheme;
+    final InputDecorationTheme inputTheme = theme.inputDecorationTheme;
+    final InputBorder inputBorder = inputTheme.border
+      ?? (useMaterial3 ? const OutlineInputBorder() : const UnderlineInputBorder());
+
     return TextFormField(
       decoration: InputDecoration(
-        border: inputTheme.border ?? const UnderlineInputBorder(),
+        border: inputBorder,
         filled: inputTheme.filled,
         hintText: widget.fieldHintText ?? localizations.dateHelpText,
         labelText: widget.fieldLabelText ?? localizations.dateInputLabel,
diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart
index 1de0831..a531f4a 100644
--- a/packages/flutter/lib/src/material/material.dart
+++ b/packages/flutter/lib/src/material/material.dart
@@ -254,6 +254,7 @@
 
   /// The color to paint the shadow below the material.
   ///
+  /// {@template flutter.material.material.shadowColor}
   /// If null and [ThemeData.useMaterial3] is true then [ThemeData]'s
   /// [ColorScheme.shadow] will be used. If [ThemeData.useMaterial3] is false
   /// then [ThemeData.shadowColor] will be used.
@@ -266,11 +267,13 @@
   ///    property if it is null.
   ///  * [ThemeData.applyElevationOverlayColor], which turns elevation overlay
   /// on or off for dark themes.
+  /// {@endtemplate}
   final Color? shadowColor;
 
   /// The color of the surface tint overlay applied to the material color
   /// to indicate elevation.
   ///
+  /// {@template flutter.material.material.surfaceTintColor}
   /// Material Design 3 introduced a new way for some components to indicate
   /// their elevation by using a surface tint color overlay on top of the
   /// base material [color]. This overlay is painted with an opacity that is
@@ -291,6 +294,7 @@
   ///     tint.
   ///   * https://m3.material.io/styles/color/the-color-system/color-roles
   ///     which specifies how the overlay is applied.
+  /// {@endtemplate}
   final Color? surfaceTintColor;
 
   /// The typographical style to use for text within this material.
@@ -298,11 +302,13 @@
 
   /// Defines the material's shape as well its shadow.
   ///
+  /// {@template flutter.material.material.shape}
   /// If shape is non null, the [borderRadius] is ignored and the material's
   /// clip boundary and shadow are defined by the shape.
   ///
   /// A shadow is only displayed if the [elevation] is greater than
   /// zero.
+  /// {@endtemplate}
   final ShapeBorder? shape;
 
   /// Whether to paint the [shape] border in front of the [child].
diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart
index 0381885..bec0bea 100644
--- a/packages/flutter/lib/src/material/theme_data.dart
+++ b/packages/flutter/lib/src/material/theme_data.dart
@@ -22,6 +22,7 @@
 import 'colors.dart';
 import 'constants.dart';
 import 'data_table_theme.dart';
+import 'date_picker_theme.dart';
 import 'dialog_theme.dart';
 import 'divider_theme.dart';
 import 'drawer_theme.dart';
@@ -348,6 +349,7 @@
     CheckboxThemeData? checkboxTheme,
     ChipThemeData? chipTheme,
     DataTableThemeData? dataTableTheme,
+    DatePickerThemeData? datePickerTheme,
     DialogTheme? dialogTheme,
     DividerThemeData? dividerTheme,
     DrawerThemeData? drawerTheme,
@@ -600,6 +602,7 @@
     checkboxTheme ??= const CheckboxThemeData();
     chipTheme ??= const ChipThemeData();
     dataTableTheme ??= const DataTableThemeData();
+    datePickerTheme ??= const DatePickerThemeData();
     dialogTheme ??= const DialogTheme();
     dividerTheme ??= const DividerThemeData();
     drawerTheme ??= const DrawerThemeData();
@@ -697,6 +700,7 @@
       checkboxTheme: checkboxTheme,
       chipTheme: chipTheme,
       dataTableTheme: dataTableTheme,
+      datePickerTheme: datePickerTheme,
       dialogTheme: dialogTheme,
       dividerTheme: dividerTheme,
       drawerTheme: drawerTheme,
@@ -810,6 +814,7 @@
     required this.checkboxTheme,
     required this.chipTheme,
     required this.dataTableTheme,
+    required this.datePickerTheme,
     required this.dialogTheme,
     required this.dividerTheme,
     required this.drawerTheme,
@@ -1238,6 +1243,7 @@
   ///     - [ActionChip] (used for Assist and Suggestion chips),
   ///     - [FilterChip], [ChoiceChip] (used for single selection filter chips),
   ///     - [InputChip]
+  ///   * Date pickers: [showDatePicker], [showDateRangePicker], [DatePickerDialog], [DateRangePickerDialog], [InputDatePickerFormField]
   ///   * Dialogs: [Dialog], [AlertDialog]
   ///   * Divider: [Divider]
   ///   * Lists: [ListTile]
@@ -1252,6 +1258,7 @@
   ///   * Switch: [Switch]
   ///   * Tabs: [TabBar]
   ///   * TextFields: [TextField] together with its [InputDecoration]
+  ///   * Time pickers: [showTimePicker], [TimePickerDialog]
   ///   * Top app bar: [AppBar]
   ///
   /// In addition, this flag enables features introduced in Android 12.
@@ -1473,6 +1480,10 @@
   /// widgets.
   final DataTableThemeData dataTableTheme;
 
+  /// A theme for customizing the appearance and layout of [DatePickerDialog]
+  /// widgets.
+  final DatePickerThemeData datePickerTheme;
+
   /// A theme for customizing the shape of a dialog.
   final DialogTheme dialogTheme;
 
@@ -1811,6 +1822,7 @@
     CheckboxThemeData? checkboxTheme,
     ChipThemeData? chipTheme,
     DataTableThemeData? dataTableTheme,
+    DatePickerThemeData? datePickerTheme,
     DialogTheme? dialogTheme,
     DividerThemeData? dividerTheme,
     DrawerThemeData? drawerTheme,
@@ -1971,6 +1983,7 @@
       checkboxTheme: checkboxTheme ?? this.checkboxTheme,
       chipTheme: chipTheme ?? this.chipTheme,
       dataTableTheme: dataTableTheme ?? this.dataTableTheme,
+      datePickerTheme: datePickerTheme ?? this.datePickerTheme,
       dialogTheme: dialogTheme ?? this.dialogTheme,
       dividerTheme: dividerTheme ?? this.dividerTheme,
       drawerTheme: drawerTheme ?? this.drawerTheme,
@@ -2165,6 +2178,7 @@
       checkboxTheme: CheckboxThemeData.lerp(a.checkboxTheme, b.checkboxTheme, t),
       chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t)!,
       dataTableTheme: DataTableThemeData.lerp(a.dataTableTheme, b.dataTableTheme, t),
+      datePickerTheme: DatePickerThemeData.lerp(a.datePickerTheme, b.datePickerTheme, t),
       dialogTheme: DialogTheme.lerp(a.dialogTheme, b.dialogTheme, t),
       dividerTheme: DividerThemeData.lerp(a.dividerTheme, b.dividerTheme, t),
       drawerTheme: DrawerThemeData.lerp(a.drawerTheme, b.drawerTheme, t)!,
@@ -2273,6 +2287,7 @@
         other.checkboxTheme == checkboxTheme &&
         other.chipTheme == chipTheme &&
         other.dataTableTheme == dataTableTheme &&
+        other.datePickerTheme == datePickerTheme &&
         other.dialogTheme == dialogTheme &&
         other.dividerTheme == dividerTheme &&
         other.drawerTheme == drawerTheme &&
@@ -2378,6 +2393,7 @@
       checkboxTheme,
       chipTheme,
       dataTableTheme,
+      datePickerTheme,
       dialogTheme,
       dividerTheme,
       drawerTheme,
@@ -2485,6 +2501,7 @@
     properties.add(DiagnosticsProperty<CheckboxThemeData>('checkboxTheme', checkboxTheme, defaultValue: defaultData.checkboxTheme, level: DiagnosticLevel.debug));
     properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme, level: DiagnosticLevel.debug));
     properties.add(DiagnosticsProperty<DataTableThemeData>('dataTableTheme', dataTableTheme, defaultValue: defaultData.dataTableTheme, level: DiagnosticLevel.debug));
+    properties.add(DiagnosticsProperty<DatePickerThemeData>('datePickerTheme', datePickerTheme, defaultValue: defaultData.datePickerTheme, level: DiagnosticLevel.debug));
     properties.add(DiagnosticsProperty<DialogTheme>('dialogTheme', dialogTheme, defaultValue: defaultData.dialogTheme, level: DiagnosticLevel.debug));
     properties.add(DiagnosticsProperty<DividerThemeData>('dividerTheme', dividerTheme, defaultValue: defaultData.dividerTheme, level: DiagnosticLevel.debug));
     properties.add(DiagnosticsProperty<DrawerThemeData>('drawerTheme', drawerTheme, defaultValue: defaultData.drawerTheme, level: DiagnosticLevel.debug));
diff --git a/packages/flutter/test/material/date_picker_theme_test.dart b/packages/flutter/test/material/date_picker_theme_test.dart
new file mode 100644
index 0000000..c204771
--- /dev/null
+++ b/packages/flutter/test/material/date_picker_theme_test.dart
@@ -0,0 +1,426 @@
+// 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/material.dart';
+import 'package:flutter_test/flutter_test.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)),
+  );
+
+  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?;
+  }
+
+  test('DatePickerThemeData copyWith, ==, hashCode basics', () {
+    expect(const DatePickerThemeData(), const DatePickerThemeData().copyWith());
+    expect(const DatePickerThemeData().hashCode, const DatePickerThemeData().copyWith().hashCode);
+  });
+
+  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);
+  });
+
+  testWidgets('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.light(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.labelMedium);
+    expect(m3.weekdayStyle, textTheme.bodySmall?.apply(color: colorScheme.onSurface));
+    expect(m3.dayStyle, textTheme.bodySmall);
+    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.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);
+  });
+
+
+  testWidgets('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.light(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.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);
+  });
+
+  testWidgets('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>[]);
+  });
+
+  testWidgets('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, <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))',
+    ]);
+  });
+
+  testWidgets('DatePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      MaterialApp(
+        theme: ThemeData.light(useMaterial3: true).copyWith(
+          datePickerTheme: datePickerTheme,
+        ),
+        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);
+
+    // 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>{}));
+  });
+
+
+  testWidgets('DateRangePickerDialog uses ThemeData datePicker theme', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      MaterialApp(
+        theme: ThemeData.light(useMaterial3: true).copyWith(
+          datePickerTheme: datePickerTheme,
+        ),
+        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 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);
+  });
+}
diff --git a/packages/flutter/test/material/theme_data_test.dart b/packages/flutter/test/material/theme_data_test.dart
index bd255e5..07eb2b4 100644
--- a/packages/flutter/test/material/theme_data_test.dart
+++ b/packages/flutter/test/material/theme_data_test.dart
@@ -781,6 +781,7 @@
       checkboxTheme: const CheckboxThemeData(),
       chipTheme: chipTheme,
       dataTableTheme: const DataTableThemeData(),
+      datePickerTheme: const DatePickerThemeData(),
       dialogTheme: const DialogTheme(backgroundColor: Colors.black),
       dividerTheme: const DividerThemeData(color: Colors.black),
       drawerTheme: const DrawerThemeData(),
@@ -901,6 +902,7 @@
       checkboxTheme: const CheckboxThemeData(),
       chipTheme: otherChipTheme,
       dataTableTheme: const DataTableThemeData(),
+      datePickerTheme: const DatePickerThemeData(backgroundColor: Colors.amber),
       dialogTheme: const DialogTheme(backgroundColor: Colors.white),
       dividerTheme: const DividerThemeData(color: Colors.white),
       drawerTheme: const DrawerThemeData(),
@@ -1008,6 +1010,7 @@
       chipTheme: otherTheme.chipTheme,
       dataTableTheme: otherTheme.dataTableTheme,
       dialogTheme: otherTheme.dialogTheme,
+      datePickerTheme: otherTheme.datePickerTheme,
       dividerTheme: otherTheme.dividerTheme,
       drawerTheme: otherTheme.drawerTheme,
       elevatedButtonTheme: otherTheme.elevatedButtonTheme,
@@ -1110,6 +1113,7 @@
     expect(themeDataCopy.checkboxTheme, equals(otherTheme.checkboxTheme));
     expect(themeDataCopy.chipTheme, equals(otherTheme.chipTheme));
     expect(themeDataCopy.dataTableTheme, equals(otherTheme.dataTableTheme));
+    expect(themeDataCopy.datePickerTheme, equals(otherTheme.datePickerTheme));
     expect(themeDataCopy.dialogTheme, equals(otherTheme.dialogTheme));
     expect(themeDataCopy.dividerTheme, equals(otherTheme.dividerTheme));
     expect(themeDataCopy.drawerTheme, equals(otherTheme.drawerTheme));
@@ -1248,6 +1252,7 @@
       'checkboxTheme',
       'chipTheme',
       'dataTableTheme',
+      'datePickerTheme',
       'dialogTheme',
       'dividerTheme',
       'drawerTheme',