Skip to content

[DatePickerThemeData] doesn't allow changes to the sub-header styling #160591

Closed

Description

@francesco-amonini

Steps to reproduce

  1. Create a Flutter app with a DatePicker widget (the fastest way is to copy-paste the code from the docs [https://api.flutter.dev/flutter/material/showDatePicker.html])
  2. Style your DatePicker using DatePickerThemeData in the MaterialApp theme
  3. There's no way to change the sub-header color

Expected results

I should be able to change the sub-header color from a DatePickerThemeData attribute.

Actual results

The only way to change the sub-header attribute is from the colorScheme section, specifying the desired color in the onSurface parameter (in the example, set to orange).

Code sample

Code sample
import 'package:flutter/material.dart';

void main() => runApp(const DatePickerApp());

class DatePickerApp extends StatelessWidget {
  const DatePickerApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      restorationScopeId: 'app',
      home: DatePickerExample(restorationId: 'main'),
      theme: ThemeData(
        colorScheme: ColorScheme.light(onSurface: Colors.orange),
        datePickerTheme: DatePickerThemeData(
          backgroundColor: Colors.white,
          // Add button style to set text color to black
          cancelButtonStyle: ButtonStyle(
            foregroundColor: WidgetStatePropertyAll(Colors.black),
          ),
          confirmButtonStyle: ButtonStyle(
            backgroundColor: WidgetStatePropertyAll(Colors.blue),
            foregroundColor: WidgetStatePropertyAll(Colors.white),
          ),
          dayBackgroundColor: WidgetStateProperty.resolveWith((states) {
            if (states.contains(WidgetState.selected)) {
              return Colors.blue;
            }
            return Colors.transparent;
          }),
          dayForegroundColor: WidgetStateProperty.resolveWith((states) {
            if (states.contains(WidgetState.selected)) {
              return Colors.white;
            }
            return Colors.black;
          }),
          headerBackgroundColor: Colors.blue,
          headerForegroundColor: Colors.white,
          inputDecorationTheme: InputDecorationTheme(
            labelStyle: TextStyle(
              color: Colors.black,
              fontSize: 16,
              fontWeight: FontWeight.w500,
            ),
            isDense: true,
            floatingLabelBehavior: FloatingLabelBehavior.auto,
            border: OutlineInputBorder(),
          ),
          todayBackgroundColor: WidgetStateProperty.resolveWith(
            (states) {
              if (states.contains(WidgetState.selected)) {
                return Colors.blue;
              }
              return Colors.transparent;
            },
          ),
          todayForegroundColor: WidgetStateProperty.resolveWith(
            (states) {
              if (states.contains(WidgetState.selected)) {
                return Colors.white;
              }
              return Colors.blue;
            },
          ),

          weekdayStyle: const TextStyle(color: Colors.black),
          yearBackgroundColor: WidgetStateProperty.resolveWith(
            (states) {
              if (states.contains(WidgetState.selected)) {
                return Colors.blue;
              }
              return Colors.transparent;
            },
          ),
          yearForegroundColor: WidgetStateProperty.resolveWith(
            (states) {
              if (states.contains(WidgetState.selected)) {
                return Colors.white;
              }
              return Colors.black;
            },
          ),
          yearOverlayColor: WidgetStatePropertyAll(Colors.blue),
        ),
      ),
    );
  }
}

class DatePickerExample extends StatefulWidget {
  const DatePickerExample({super.key, this.restorationId});

  final String? restorationId;

  @override
  State<DatePickerExample> createState() => _DatePickerExampleState();
}

/// RestorationProperty objects can be used because of RestorationMixin.
class _DatePickerExampleState extends State<DatePickerExample>
    with RestorationMixin {
  // In this example, the restoration ID for the mixin is passed in through
  // the [StatefulWidget]'s constructor.
  @override
  String? get restorationId => widget.restorationId;

  final RestorableDateTime _selectedDate =
      RestorableDateTime(DateTime(2021, 7, 25));
  late final RestorableRouteFuture<DateTime?> _restorableDatePickerRouteFuture =
      RestorableRouteFuture<DateTime?>(
    onComplete: _selectDate,
    onPresent: (NavigatorState navigator, Object? arguments) {
      return navigator.restorablePush(
        _datePickerRoute,
        arguments: _selectedDate.value.millisecondsSinceEpoch,
      );
    },
  );

  @pragma('vm:entry-point')
  static Route<DateTime> _datePickerRoute(
    BuildContext context,
    Object? arguments,
  ) {
    return DialogRoute<DateTime>(
      context: context,
      builder: (BuildContext context) {
        return DatePickerDialog(
          restorationId: 'date_picker_dialog',
          initialEntryMode: DatePickerEntryMode.calendarOnly,
          initialDate: DateTime.fromMillisecondsSinceEpoch(arguments! as int),
          firstDate: DateTime(2021),
          lastDate: DateTime(2022),
        );
      },
    );
  }

  @override
  void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
    registerForRestoration(_selectedDate, 'selected_date');
    registerForRestoration(
        _restorableDatePickerRouteFuture, 'date_picker_route_future');
  }

  void _selectDate(DateTime? newSelectedDate) {
    if (newSelectedDate != null) {
      setState(() {
        _selectedDate.value = newSelectedDate;
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text(
              'Selected: ${_selectedDate.value.day}/${_selectedDate.value.month}/${_selectedDate.value.year}'),
        ));
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: OutlinedButton(
          onPressed: () {
            _restorableDatePickerRouteFuture.present();
          },
          child: const Text('Open Date Picker'),
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

image

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]

Activity

  1. changed the title [-][DatePickerThemeData] doesn't allor [/-] [+][DatePickerThemeData] doesn't allow changes to the sub-header styling[/+] on Dec 19, 2024
  2. francesco-amonini commented on Dec 19, 2024

    @francesco-amonini
    Author

    The problem resides at line 787 of the file calendar_date_picker.dart of the flutter/material.dart package, where the color is defined as follow:

    final Color controlColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.60);

    not allowing a proper styling from the DatePickerThemeData

  3. maheshj01 commented on Dec 20, 2024

    @maheshj01
    Member

    Hi @francesco-amonini, Thanks for filing the issue. I did try wrapping DatePickerDialog with a theme but there doesn't seem to be an API to customize the subheading (one with month/ year)

    Screenshot 2024-12-20 at 00 11 07
    Theme(
        data: Theme.of(context).copyWith(
            colorScheme: Theme.of(context).colorScheme.copyWith(
                  primary: Colors.amber,
                  onSurface: Colors.green,
                  onPrimary: Colors.red,
                  secondary: Colors.blue,
                  onSecondary: Colors.purple,
                )),
        child: DatePickerDialog(
          restorationId: 'date_picker_dialog',
          initialEntryMode: DatePickerEntryMode.calendarOnly,
          initialDate:
              DateTime.fromMillisecondsSinceEpoch(arguments! as int),
          firstDate: DateTime(2021),
          lastDate: DateTime(2022),
        ));
    },

    Labeling this as a proposal to allow customizing subheaderColor

  4. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    c: proposalA detailed proposal for a change to Flutter
    and removed
    in triagePresently being triaged by the triage team
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    on Dec 20, 2024
  5. added
    team-designOwned by Design Languages team
    and removed on Dec 30, 2024
  6. 26 remaining items

  7. tirth-patel-nc commented on Jun 6, 2025

    @tirth-patel-nc
    Member

    this seems to be fixed by #164102, can we close this?

  8. divyaTyagi123 commented on Aug 21, 2025

    @divyaTyagi123

    Hi, I’d like to work on this issue. Can I be assigned?

  9. tirth-patel-nc commented on Aug 21, 2025

    @tirth-patel-nc
  10. tirth-patel-nc commented on Aug 21, 2025

    @tirth-patel-nc
    Member

    I believe it has been fixed #160591 (comment)

  11. divyaTyagi123 commented on Aug 21, 2025

    @divyaTyagi123
  12. QuncCccccc commented on Mar 4, 2026

    @QuncCccccc
    Contributor

    Seems this has been fixed by #164102.

  13. added
    r: fixedIssue is closed as already fixed in a newer version
    on Mar 5, 2026
  14. github-actions commented on Mar 19, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  15. locked as resolved and limited conversation to collaborators on Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: proposalA detailed proposal for a change to Flutterf: date/time pickerDate or time picker widgetsframeworkflutter/packages/flutter repository. See also f: labels.p: material_uimaterial_ui package in flutter/packagesr: fixedIssue is closed as already fixed in a newer versionteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions