From 2f74e30511d4185b2e77def9750825d5bbcefc72 Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Mon, 5 Oct 2020 16:05:48 -0700 Subject: [PATCH] Fixed a problem with invalid dates when switching back to calendar mode in the date range picker. (#67159) --- .../pickers/date_range_picker_dialog.dart | 13 +++++- .../pickers/input_date_range_picker.dart | 8 ++-- .../test/material/date_range_picker_test.dart | 46 +++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart b/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart index f31e159feea..6ae67996f08 100644 --- a/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart +++ b/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart @@ -303,6 +303,17 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> { break; case DatePickerEntryMode.input: + // Validate the range dates + if (_selectedStart != null && + (_selectedStart!.isBefore(widget.firstDate) || _selectedStart!.isAfter(widget.lastDate))) { + _selectedStart = null; + // With no valid start date, having an end date makes no sense for the UI. + _selectedEnd = null; + } + if (_selectedEnd != null && + (_selectedEnd!.isBefore(widget.firstDate) || _selectedEnd!.isAfter(widget.lastDate))) { + _selectedEnd = null; + } // If invalid range (start after end), then just use the start date if (_selectedStart != null && _selectedEnd != null && _selectedStart!.isAfter(_selectedEnd!)) { _selectedEnd = null; @@ -313,7 +324,7 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> { }); } - void _handleStartDateChanged(DateTime date) { + void _handleStartDateChanged(DateTime? date) { setState(() => _selectedStart = date); } diff --git a/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart b/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart index c5d3dfdf531..9b161b3d0b1 100644 --- a/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart +++ b/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart @@ -64,10 +64,10 @@ class InputDateRangePicker extends StatefulWidget { final DateTime lastDate; /// Called when the user changes the start date of the selected range. - final ValueChanged? onStartDateChanged; + final ValueChanged? onStartDateChanged; /// Called when the user changes the end date of the selected range. - final ValueChanged? onEndDateChanged; + final ValueChanged? onEndDateChanged; /// The text that is displayed at the top of the header. /// @@ -205,7 +205,7 @@ class InputDateRangePickerState extends State { setState(() { _startInputText = text; _startDate = _parseDate(text); - widget.onStartDateChanged?.call(_startDate!); + widget.onStartDateChanged?.call(_startDate); }); if (widget.autovalidate) { validate(); @@ -216,7 +216,7 @@ class InputDateRangePickerState extends State { setState(() { _endInputText = text; _endDate = _parseDate(text); - widget.onEndDateChanged?.call(_endDate!); + widget.onEndDateChanged?.call(_endDate); }); if (widget.autovalidate) { validate(); diff --git a/packages/flutter/test/material/date_range_picker_test.dart b/packages/flutter/test/material/date_range_picker_test.dart index 890308b695a..10eafb0fc7f 100644 --- a/packages/flutter/test/material/date_range_picker_test.dart +++ b/packages/flutter/test/material/date_range_picker_test.dart @@ -256,6 +256,52 @@ void main() { }); }); + group('Toggle from input entry mode validates dates', () { + setUp(() { + initialEntryMode = DatePickerEntryMode.input; + }); + + testWidgets('Invalid start date', (WidgetTester tester) async { + // Invalid start date should have neither a start nor end date selected in + // calendar mode + await preparePicker(tester, (Future range) async { + await tester.enterText(find.byType(TextField).at(0), '12/27/1918'); + await tester.enterText(find.byType(TextField).at(1), '12/25/2016'); + await tester.tap(find.byIcon(Icons.calendar_today)); + await tester.pumpAndSettle(); + + expect(find.text('Start Date'), findsOneWidget); + expect(find.text('End Date'), findsOneWidget); + }); + }); + + testWidgets('Invalid end date', (WidgetTester tester) async { + // Invalid end date should only have a start date selected + await preparePicker(tester, (Future range) async { + await tester.enterText(find.byType(TextField).at(0), '12/24/2016'); + await tester.enterText(find.byType(TextField).at(1), '12/25/2050'); + await tester.tap(find.byIcon(Icons.calendar_today)); + await tester.pumpAndSettle(); + + expect(find.text('Dec 24'), findsOneWidget); + expect(find.text('End Date'), findsOneWidget); + }); + }); + + testWidgets('Invalid range', (WidgetTester tester) async { + // Start date after end date should just use the start date + await preparePicker(tester, (Future range) async { + await tester.enterText(find.byType(TextField).at(0), '12/25/2016'); + await tester.enterText(find.byType(TextField).at(1), '12/24/2016'); + await tester.tap(find.byIcon(Icons.calendar_today)); + await tester.pumpAndSettle(); + + expect(find.text('Dec 25'), findsOneWidget); + expect(find.text('End Date'), findsOneWidget); + }); + }); + }); + testWidgets('OK Cancel button layout', (WidgetTester tester) async { Widget buildFrame(TextDirection textDirection) { return MaterialApp(