Skip to content

Implementation of the Material Date Range Picker. #55939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 8, 2020
Merged

Implementation of the Material Date Range Picker. #55939

merged 12 commits into from
May 8, 2020

Conversation

darrenaustin
Copy link
Contributor

Description

This PR adds support for the Material Date Range Picker to Flutter's Material library.

DateRangePicker

This includes support for both the full screen modal dialog with a scrolling calendar grid, as well as a text input mode that has two input fields. The new API consists mostly of the function:

Future<DateTimeRange> showDateRangePicker({
  @required BuildContext context,
  DateTimeRange initialDateRange,
  @required DateTime firstDate,
  @required DateTime lastDate,
  DateTime currentDate,
  DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
  String helpText,
  String cancelText,
  String confirmText,
  String saveText,
  Locale locale,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
  TextDirection textDirection,
  TransitionBuilder builder,
  String errorFormatText,
  String errorInvalidText,
  String errorInvalidRangeText,
  String fieldStartHintText,
  String fieldEndHintText,
  String fieldStartLabelText,
  String fieldEndLabelText,
}) async

Which is similar to showDatePicker, but returns a new DateTimeRange that describes the start and end dates of the range.

Kudos to @rami-a, upon whose work this is based.

Related Issues

Fixes: #50618

Tests

Added a new date_range_picker_test.dart file with tests for the various features of the new picker.

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I signed the [CLA].
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them?

  • No, no existing tests failed, so this is not a breaking change.

@fluttergithubbot fluttergithubbot added f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. labels Apr 29, 2020
Copy link
Contributor

@rami-a rami-a left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks great, just a few nits and comments. Additionally found a few UI issues outlined below:

  • The full screen modal should extend to the safe area
    Simulator Screen Shot - iPhone 11 Pro - 2020-04-29 at 10 14 40
    Simulator Screen Shot - iPhone 11 Pro - 2020-04-29 at 10 22 15

  • The selected range text should go to 2 lines on the input mode in landscape mode.
    Simulator Screen Shot - iPhone 11 Pro - 2020-04-29 at 10 18 22

  • Picker overflows when keyboard is up in landscape
    Simulator Screen Shot - iPhone 11 Pro - 2020-04-29 at 10 20 26

actions: <Widget>[
if (orientation == Orientation.landscape) entryModeIcon,
ButtonTheme(
minWidth: 64,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be consistent with usage of .0 for doubles

///
/// Note: this is not publicly exported (see pickers.dart), as it is just an
/// internal component used by [showDateRangePicker].
class CalendarDateRangePicker extends StatefulWidget {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be a private class inside of another file? People can still include this directly, even though it isn't exported, of course.

Or is it for testing purposes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was mostly because I wasn't yet ready to commit to it being part of the public API, but didn't want to add it's bulk to the already long date_range_picker_dialog.dart. I really wish dart had a 'package private' access control for things like this.

Copy link
Contributor

@gspencergoog gspencergoog Apr 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it does have "library private", but the library can only be extended to multiple files with the part of directive, which we discourage. I know what you mean though: it would be nice if things could be made private to anything in the pub package.

Like it or not, it's part of the public API, it just isn't exported with the top level include points. Anyone can use this class with:

import 'package:/flutter/src/material/pickers/calendar_date_range_picker.dart';

Sure, it's not likely, but they could.

Anyhow, it's not a big deal: I suspect that a) people are on their own if they do that, and 2) that we might want to make this a public API anyhow.

If you're not ready to make it public, though, it's probably OK to put it in the other huge file and make it private. We have plenty of those in Flutter (mostly for that reason) already. widgets/basic.dart is 7,144 lines...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I mostly landed on "a) people are on their own if they do that". It is called out as a "DON'T" in the effective dart guide (https://dart.dev/guides/language/effective-dart/usage#dont-import-libraries-that-are-inside-the-src-directory-of-another-package), but I totally get the point that it is technically still public.

I am leaning toward leaving this way for now. That will make it slightly easier if we do want to make it public at some point.


/// A `TextInputFormatter` set up to format dates.
///
/// Note: this is not publicly exported (see pickers.dart), as it is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not private?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is shared between the InputDatePickerFormField and the InputDateRangePicker which is in another file. I could move the range picker into the input_date_picker.dart file, but InputDateRangePicker is not a public API either.

Again a package private feature would be welcome. I didn't see many examples of how to handle this sort of situation elsewhere in the framework. Most components are as complicated as these date pickers, so they just put everything in one large files. With the date pickers that just seemed too unwieldy.

final WhitelistingTextInputFormatter _filterFormatter =
// Only allow digits and separators (slash, dot, comma, hyphen, space).
WhitelistingTextInputFormatter(RegExp(r'[\d\/\.,-\s]+'));
/// Formatter that will filter out all characters except digits and date separators.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super-meta nit: doesn't have to be a doc comment. Fine to leave it though.

@darrenaustin
Copy link
Contributor Author

Code looks great, just a few nits and comments. Additionally found a few UI issues outlined below:

Thanks @rami-a, I just pushed changes that I think address everything you brought up except for the landscape input dialog overflowing. Still looking into that one. What device where you seeing it on?

@rami-a
Copy link
Contributor

rami-a commented May 1, 2020

Code looks great, just a few nits and comments. Additionally found a few UI issues outlined below:

Thanks @rami-a, I just pushed changes that I think address everything you brought up except for the landscape input dialog overflowing. Still looking into that one. What device where you seeing it on?

I was seeing it on the iPhone 11 pro simulator, though I also saw it on the regular date picker under the same conditions.

@rami-a
Copy link
Contributor

rami-a commented May 4, 2020

Code looks great, just a few nits and comments. Additionally found a few UI issues outlined below:

Thanks @rami-a, I just pushed changes that I think address everything you brought up except for the landscape input dialog overflowing. Still looking into that one. What device where you seeing it on?

I was seeing it on the iPhone 11 pro simulator, though I also saw it on the regular date picker under the same conditions.

One possible solution, could be to put the header into a SingleChildScrollView to allow for it to scroll in landscape?

@rami-a
Copy link
Contributor

rami-a commented May 4, 2020

Found one more small issue, the Picker Dialog's content should respect the safe area.

Simulator Screen Shot - iPhone 11 Pro - 2020-05-04 at 10 35 15

Copy link
Contributor

@rami-a rami-a left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM once those last 2 issues are fixed

@darrenaustin
Copy link
Contributor Author

Found one more small issue, the Picker Dialog's content should respect the safe area.

Didn't you just complain that the dialog should cover the safe area? 😏

Kidding aside, I am a little confused at the expectations here. Is it just the content area that should honor the safe area, but the dialog as a whole shouldn't? Or is it just the bottom safe area that is of concern?

@darrenaustin
Copy link
Contributor Author

One possible solution, could be to put the header into a SingleChildScrollView to allow for it to scroll in landscape?

Possibly. I have been playing with this for a while, and the main problem is that on iOS the keyboard that we bring up for TextInputType.datetime is the full alphanumeric keyboard that fills over half the vertical space on a iPhone 11 in landscape. That leads to very little space for us. We could switch over to the numeric keypad which is shorter, but it lacks any separators needed for dates. I may try to put it in a scroll view if I can't find another way to make it fit.

@rami-a
Copy link
Contributor

rami-a commented May 5, 2020

Found one more small issue, the Picker Dialog's content should respect the safe area.

Didn't you just complain that the dialog should cover the safe area? 😏

Kidding aside, I am a little confused at the expectations here. Is it just the content area that should honor the safe area, but the dialog as a whole shouldn't? Or is it just the bottom safe area that is of concern?

haha yes I realize that it sounds like I'm changing my mind. But yeah the entire dialog should extend into the safe area so that you can't see the page behind it. However the content area should honor the safe area, should be as simple as wrapping the Scaffold's body with a SafeArea

Copy link
Contributor

@HansMuller HansMuller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return TextFormField(
decoration: InputDecoration(
border: const UnderlineInputBorder(),
filled: true,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darrenaustin Why is this hardcoded? If I have InputDecorationTheme with filled: false, I expect it to be false, but now it's not the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is following the Material Design for this picker, which calls for it to be filled. That said, we should probably defer to the ambient InputDecorationTheme if one is set for this. We have a future theming pass that we will be doing on the date pickers that will allow for more customization, but I might be able to put together a small PR to fix this one issue.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darrenaustin Thanks. It would be great for my use case.

@jhionan
Copy link

jhionan commented Sep 2, 2020

Why not exporting the CalendarDateRangePicker class, like CalendarDatePicker, so we can create our custom date range picker, as it was merged we don't have access to the widget, only for the showRangeDatePicker() function. I with to customize the picker as my design did. I cant see any reason why not puting the CalendarDateRangePicker class exported at public api too.

@darrenaustin
Copy link
Contributor Author

Hi @jhionan

Why not exporting the CalendarDateRangePicker class, like CalendarDatePicker, so we can create our custom date range picker

I did consider this, but time constraints didn't allow me to make this component ready for exposure as a publicly supported API. It is definitely on my list of things to revisit. Please open a feature request issue so we can track this. Thx.

@jhionan
Copy link

jhionan commented Sep 2, 2020

I exported your widget for my use, and its very functional as it is, in my case I just want the CalendarRangeDatePicker class. The only suggestion I have for a public api is making it easier to change colors. I know it can be done by changing the theme, but I feel that would be great if it also have a straight forward color selection.

And good job!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Natively support a date range picker
8 participants