-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Prevent out of bound date picker selections #7773
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
Conversation
|
Soliciting some feedback before I write the tests |
| assert(currentDate != null); | ||
| assert(onChanged != null); | ||
| assert(displayedMonth != null); | ||
| assert(lastDate.isAfter(firstDate)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can they not be the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I kept the previous behaviour but makes sense. Changed.
| if (day < 1) { | ||
| labels.add(new Container()); | ||
| } else { | ||
| DateTime dayToBuild = new DateTime(year, month, day); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
| behavior: HitTestBehavior.opaque, | ||
| onTap: () { | ||
| DateTime result = new DateTime(year, month, day); | ||
| onChanged(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You already have dayToBuild, might as well just return it instead of making a new copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
| } | ||
|
|
||
| /// True if the earliest allowable month is displayed. | ||
| bool get _onFirstMonth { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually use the "on" prefix for callback closures. Maybe _isDisplayingFirstMonth ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, makes sense
| void initState() { | ||
| super.initState(); | ||
| // Initially display the pre-selected date. | ||
| _currentDisplayedMonthDate = new DateTime(config.selectedDate.year, config.selectedDate.month); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to update this member in didUpdateConfig as well. When your parent builds a new widget for this location in the tree and this State object is re-used, the framework calls didUpdateConfig to let you know that your config property now points to the new widget.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Thanks.
| new DateTime(config.lastDate.year, config.lastDate.month)); | ||
| } | ||
|
|
||
| void _monthPageChanged(int monthPage) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually name implementations of onFoo callbacks something like _handleFoo, so in this case _handleMonthPageChanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SG
| ScrollableState state = _dayPickerListKey.currentState; | ||
| double newPage = state.scrollOffset.round() + 1.0; | ||
| state?.scrollTo(newPage, duration: _kMonthScrollDuration); | ||
| _monthPageChanged(newPage.toInt()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to call _monthPageChanged here. The scrollTo call will eventually cause the callback to be called for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, what's what I was thinking when I opened #7764 since it wasn't working. If it's meant to, I'll dig a bit into why it wasn't working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I read correctly, seems like onPageChanged is a PageableState concept and scrollTo is a ScrollableState concept. And the various stuff on the call back chains of scrollTo doesn't re-enter PageableState.
I can either use APIs on PageableState directly in date_picker, override scrollTo on the way in and await then snap to page and notify, add some handle to super's end scroll, the offset setter or something in PageableState, or just always callback on didUpdateConfig.
I suspect calling pageable state APIs directly is probably best. Overriding would imply page users would lose low level scrollable programmatic APIs to scroll and not snap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, used the PageableState's fling API. PTAL
| ScrollableState state = _dayPickerListKey.currentState; | ||
| double newPage = state.scrollOffset.round() - 1.0; | ||
| state?.scrollTo(newPage, duration: _kMonthScrollDuration); | ||
| _monthPageChanged(newPage.toInt()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
|
Definitely looks in the right track. |
|
Regarding onFoo/didFoo/handleFoo, there's a discussion in the style guide. |
|
Ok, adding tests |
| Future<Null> _flingToAdjacentItem(double scrollVelocity) { | ||
| final double newScrollOffset = snapScrollOffset(scrollOffset + scrollVelocity.sign) | ||
| .clamp(snapScrollOffset(scrollOffset - 0.5), snapScrollOffset(scrollOffset + 0.5)); | ||
| .clamp(snapScrollOffset(scrollOffset - 0.50001), snapScrollOffset(scrollOffset + 0.5)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a bug. The fix is a bit cheesy but I hear you're going to delete all this so I didn't make it too fancy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure you have a test for this specific case (with the date picker), and we'll make sure we don't regress it. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's somewhere in date_picker_test.dart :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent.
| }); | ||
| }); | ||
|
|
||
| testWidgets('Cannot selecte a month past last date', (WidgetTester tester) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: here and below - typo "selecte"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, thanks
| state?.scrollTo(state.scrollOffset.round() + 1.0, duration: _kMonthScrollDuration); | ||
| if (!_isDisplayingLastMonth) { | ||
| PageableState<PageableLazyList> state = _dayPickerListKey.currentState; | ||
| state?.fling(1.0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've got the type parameter in the key, so you can write this as _dayPickerListKey.currentState?.fling(1.0) if you like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
| ScrollableState state = _dayPickerListKey.currentState; | ||
| state?.scrollTo(state.scrollOffset.round() - 1.0, duration: _kMonthScrollDuration); | ||
| if (!_isDisplayingFirstMonth) { | ||
| PageableState<PageableLazyList> state = _dayPickerListKey.currentState; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
|
Thanks! |
|
Thanks for the reviews! |

Uh oh!
There was an error while loading. Please reload this page.