showMarkedDatePicker function

Future<DateTime?> showMarkedDatePicker({
  1. required BuildContext context,
  2. required DateTime initialDate,
  3. required DateTime firstDate,
  4. required DateTime lastDate,
  5. DateTime? currentDate,
  6. DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
  7. SelectableDayPredicate? selectableDayPredicate,
  8. String? helpText,
  9. String? cancelText,
  10. String? confirmText,
  11. Locale? locale,
  12. bool useRootNavigator = true,
  13. RouteSettings? routeSettings,
  14. TextDirection? textDirection,
  15. TransitionBuilder? builder,
  16. DatePickerMode initialDatePickerMode = DatePickerMode.day,
  17. String? errorFormatText,
  18. String? errorInvalidText,
  19. String? fieldHintText,
  20. String? fieldLabelText,
  21. List<DateTime> markedDates = const <DateTime>[],
  22. UpdateMonthCallback? updateMonthCallback,
  23. ValueListenable<List<DateTime>>? markedDatesListenable,
  24. Widget? marking,
  25. Color? todayColor,
  26. Color? markedColor,
  27. Color? selectedDayColor,
  28. Color? selectedDayBackground,
  29. Offset? anchorPoint,
})

Shows a dialog containing a Material Design date picker.

The returned Future resolves to the date selected by the user when the user confirms the dialog. If the user cancels the dialog, null is returned.

When the date picker is first displayed, it will show the month of initialDate, with initialDate selected.

The firstDate is the earliest allowable date. The lastDate is the latest allowable date. initialDate must either fall between these dates, or be equal to one of them. For each of these DateTime parameters, only their dates are considered. Their time fields are ignored. They must all be non-null.

The currentDate represents the current day (i.e. today). This date will be highlighted in the day grid. If null, the date of DateTime.now() will be used.

An optional initialEntryMode argument can be used to display the date picker in the DatePickerEntryMode.calendar (a calendar month grid) or DatePickerEntryMode.input (a text input field) mode. It defaults to DatePickerEntryMode.calendar and must be non-null.

An optional selectableDayPredicate function can be passed in to only allow certain days for selection. If provided, only the days that selectableDayPredicate returns true for will be selectable. For example, this can be used to only allow weekdays for selection. If provided, it must return true for initialDate.

The following optional string parameters allow you to override the default text used for various parts of the dialog:

  • helpText, label displayed at the top of the dialog.
  • cancelText, label on the cancel button.
  • confirmText, label on the ok button.
  • errorFormatText, message used when the input text isn't in a proper date format.
  • errorInvalidText, message used when the input text isn't a selectable date.
  • fieldHintText, text used to prompt the user when no text has been entered in the field.
  • fieldLabelText, label for the date text input field.

An optional locale argument can be used to set the locale for the date picker. It defaults to the ambient locale provided by Localizations.

An optional textDirection argument can be used to set the text direction (TextDirection.ltr or TextDirection.rtl) for the date picker. It defaults to the ambient text direction provided by Directionality. If both locale and textDirection are non-null, textDirection overrides the direction chosen for the locale.

The context, useRootNavigator and routeSettings arguments are passed to showDialog, the documentation for which discusses how it is used. context and useRootNavigator must be non-null.

The builder parameter can be used to wrap the dialog widget to add inherited widgets like Theme.

An optional initialDatePickerMode argument can be used to have the calendar date picker initially appear in the DatePickerMode.year or DatePickerMode.day mode. It defaults to DatePickerMode.day, and must be non-null.

State Restoration

Using this method will not enable state restoration for the date picker. In order to enable state restoration for a date picker, use Navigator.restorablePush or Navigator.restorablePushNamed with DatePickerDialog.

For more information about state restoration, see RestorationManager.

To test state restoration on Android:

  1. Turn on "Don't keep activities", which destroys the Android activity as soon as the user leaves it. This option should become available when Developer Options are turned on for the device.
  2. Run the code sample on an Android device.
  3. Create some in-memory state in the app on the phone, e.g. by navigating to a different screen.
  4. Background the Flutter app, then return to it. It will restart and restore its state.

To test state restoration on iOS:

  1. Open ios/Runner.xcworkspace/ in Xcode.
  2. (iOS 14+ only): Switch to build in profile or release mode, as launching an app from the home screen is not supported in debug mode.
  3. Press the Play button in Xcode to build and run the app.
  4. Create some in-memory state in the app on the phone, e.g. by navigating to a different screen.
  5. Background the app on the phone, e.g. by going back to the home screen.
  6. Press the Stop button in Xcode to terminate the app while running in the background.
  7. Open the app again on the phone (not via Xcode). It will restart and restore its state.

{@tool sample --template=stateful_widget_restoration_material}

This sample demonstrates how to create a restorable Material date picker. This is accomplished by enabling state restoration by specifying MaterialApp.restorationScopeId and using Navigator.restorablePush to push DatePickerDialog when the button is tapped.

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

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, 1, 1),
        lastDate: DateTime(2022, 1, 1),
      );
    },
  );
}

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

{@end-tool}

See also:

Implementation

Future<DateTime?> showMarkedDatePicker({
  required BuildContext context,
  required DateTime initialDate,
  required DateTime firstDate,
  required DateTime lastDate,
  DateTime? currentDate,
  DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
  SelectableDayPredicate? selectableDayPredicate,
  String? helpText,
  String? cancelText,
  String? confirmText,
  Locale? locale,
  bool useRootNavigator = true,
  RouteSettings? routeSettings,
  TextDirection? textDirection,
  TransitionBuilder? builder,
  DatePickerMode initialDatePickerMode = DatePickerMode.day,
  String? errorFormatText,
  String? errorInvalidText,
  String? fieldHintText,
  String? fieldLabelText,
  List<DateTime> markedDates = const <DateTime>[],
  UpdateMonthCallback? updateMonthCallback,
  ValueListenable<List<DateTime>>? markedDatesListenable,
  Widget? marking,
  Color? todayColor,
  Color? markedColor,
  Color? selectedDayColor,
  Color? selectedDayBackground,
  Offset? anchorPoint,
}) async {
  initialDate = DateUtils.dateOnly(initialDate);
  firstDate = DateUtils.dateOnly(firstDate);
  lastDate = DateUtils.dateOnly(lastDate);
  assert(
    !lastDate.isBefore(firstDate),
    'lastDate $lastDate must be on or after firstDate $firstDate.',
  );
  assert(
    !initialDate.isBefore(firstDate),
    'initialDate $initialDate must be on or after firstDate $firstDate.',
  );
  assert(
    !initialDate.isAfter(lastDate),
    'initialDate $initialDate must be on or before lastDate $lastDate.',
  );
  assert(
    selectableDayPredicate == null || selectableDayPredicate(initialDate),
    'Provided initialDate $initialDate must satisfy provided selectableDayPredicate.',
  );

  assert(debugCheckHasMaterialLocalizations(context));

  Widget dialog = MarkedDatePickerDialog(
    initialDate: initialDate,
    firstDate: firstDate,
    lastDate: lastDate,
    currentDate: currentDate,
    initialEntryMode: initialEntryMode,
    selectableDayPredicate: selectableDayPredicate,
    helpText: helpText,
    cancelText: cancelText,
    confirmText: confirmText,
    initialCalendarMode: initialDatePickerMode,
    errorFormatText: errorFormatText,
    errorInvalidText: errorInvalidText,
    fieldHintText: fieldHintText,
    fieldLabelText: fieldLabelText,
    updateMonthCallback: updateMonthCallback,
    markedDatesListenable: markedDatesListenable,
    marking: marking,
    todayColor: todayColor,
    markedColor: markedColor,
    selectedDayColor: selectedDayColor,
    selectedDayBackground: selectedDayBackground,
  );

  if (textDirection != null) {
    dialog = Directionality(
      textDirection: textDirection,
      child: dialog,
    );
  }

  if (locale != null) {
    dialog = Localizations.override(
      context: context,
      locale: locale,
      child: dialog,
    );
  }

  return showDialog<DateTime>(
    context: context,
    useRootNavigator: useRootNavigator,
    routeSettings: routeSettings,
    builder: (BuildContext context) {
      return builder == null ? dialog : builder(context, dialog);
    },
    anchorPoint: anchorPoint,
  );
}