From a872514ad83a90ca59e3922b0d024c7db8a74d03 Mon Sep 17 00:00:00 2001 From: Marco Scannadinari <4211302+marcoms@users.noreply.github.com> Date: Wed, 6 Feb 2019 22:11:29 +0000 Subject: [PATCH] Fix immediately overriding the user's chosen AM/PM selection (#26904) --- .../lib/src/cupertino/date_picker.dart | 53 +++++++++++----- .../test/cupertino/date_picker_test.dart | 62 +++++++++++++++++-- 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/date_picker.dart b/packages/flutter/lib/src/cupertino/date_picker.dart index 069358af23..cb43389a3c 100644 --- a/packages/flutter/lib/src/cupertino/date_picker.dart +++ b/packages/flutter/lib/src/cupertino/date_picker.dart @@ -351,16 +351,35 @@ class _CupertinoDatePickerDateTimeState extends State { // in the widget after first build is ignored. DateTime initialDateTime; - // The currently selected values of the date picker. - int selectedDayFromInitial; // The difference in days between the initial date and the currently selected date. + // The difference in days between the initial date and the currently selected date. + int selectedDayFromInitial; + + // The current selection of the hour picker. + // + // If [widget.use24hFormat] is true, values range from 1-24. Otherwise values + // range from 1-12. int selectedHour; + + // The previous selection index of the hour column. + // + // This ranges from 0-23 even if [widget.use24hFormat] is false. As a result, + // it can be used for determining if we just changed from AM -> PM or vice + // versa. + int previousHourIndex; + + // The current selection of the minute picker. Values range from 0 to 59. int selectedMinute; - int selectedAmPm; // 0 means AM, 1 means PM. + + // The current selection of the AM/PM picker. + // + // - 0 means AM + // - 1 means PM + int selectedAmPm; // The controller of the AM/PM column. FixedExtentScrollController amPmController; - // Estimated width of columns. + // The estimated width of columns. final Map estimatedColumnWidths = {}; @override @@ -380,6 +399,8 @@ class _CupertinoDatePickerDateTimeState extends State { amPmController = FixedExtentScrollController(initialItem: selectedAmPm); } + + previousHourIndex = selectedHour; } @override @@ -477,25 +498,29 @@ class _CupertinoDatePickerDateTimeState extends State { if (widget.use24hFormat) { selectedHour = index; widget.onDateTimeChanged(_getDateTime()); - } - else { - final int currentHourIn24h = selectedHour + selectedAmPm * 12; + } else { + selectedHour = index % 12; + // Automatically scrolls the am/pm column when the hour column value - // goes far enough. This behavior is similar to - // iOS picker version. - if (currentHourIn24h ~/ 12 != index ~/ 12) { - selectedHour = index % 12; + // goes far enough. + + final bool wasAm = previousHourIndex >=0 && previousHourIndex <= 11; + final bool isAm = index >= 0 && index <= 11; + + if (wasAm != isAm) { + // Animation values obtained by comparing with iOS version. amPmController.animateToItem( 1 - amPmController.selectedItem, - duration: const Duration(milliseconds: 300), // Set by comparing with iOS version. + duration: const Duration(milliseconds: 300), curve: Curves.easeOut, - ); // Set by comparing with iOS version. + ); } else { - selectedHour = index % 12; widget.onDateTimeChanged(_getDateTime()); } } + + previousHourIndex = index; }, children: List.generate(24, (int index) { int hour = index; diff --git a/packages/flutter/test/cupertino/date_picker_test.dart b/packages/flutter/test/cupertino/date_picker_test.dart index 027d687528..ae9f024dec 100644 --- a/packages/flutter/test/cupertino/date_picker_test.dart +++ b/packages/flutter/test/cupertino/date_picker_test.dart @@ -7,6 +7,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/semantics.dart'; import 'package:flutter_test/flutter_test.dart'; +// scrolling by this offset will move the picker to the next item +const Offset _kRowOffset = Offset(0.0, -32.0); + void main() { group('Countdown timer picker', () { testWidgets('onTimerDurationChanged is not null', (WidgetTester tester) async { @@ -524,6 +527,53 @@ void main() { ); }); + testWidgets('picker persists am/pm value when scrolling hours', (WidgetTester tester) async { + DateTime date; + await tester.pumpWidget( + CupertinoApp( + home: SizedBox( + height: 400.0, + width: 400.0, + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + onDateTimeChanged: (DateTime newDate) { + date = newDate; + }, + initialDateTime: DateTime(2019, 1, 1, 3), + ), + ), + ), + ); + + // 3:00 -> 15:00 + await tester.drag(find.text('AM'), _kRowOffset); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 500)); + + expect(date, DateTime(2019, 1, 1, 15)); + + // 15:00 -> 16:00 + await tester.drag(find.text('3'), _kRowOffset); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 500)); + + expect(date, DateTime(2019, 1, 1, 16)); + + // 16:00 -> 4:00 + await tester.drag(find.text('PM'), -_kRowOffset); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 500)); + + expect(date, DateTime(2019, 1, 1, 4)); + + // 4:00 -> 3:00 + await tester.drag(find.text('4'), -_kRowOffset); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 500)); + + expect(date, DateTime(2019, 1, 1, 3)); + }); + testWidgets('picker automatically scrolls the am/pm column when the hour column changes enough', (WidgetTester tester) async { DateTime date; await tester.pumpWidget( @@ -542,25 +592,29 @@ void main() { ), ); - await tester.drag(find.text('11'), const Offset(0.0, -32.0)); + // 11:59 -> 12:59 + await tester.drag(find.text('11'), _kRowOffset); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(date, DateTime(2018, 1, 1, 12, 59)); - await tester.drag(find.text('12'), const Offset(0.0, 32.0)); + // 12:59 -> 11:59 + await tester.drag(find.text('12'), -_kRowOffset); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(date, DateTime(2018, 1, 1, 11, 59)); - await tester.drag(find.text('11'), const Offset(0.0, 64.0)); + // 11:59 -> 9:59 + await tester.drag(find.text('11'), -_kRowOffset * 2); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(date, DateTime(2018, 1, 1, 9, 59)); - await tester.drag(find.text('9'), const Offset(0.0, -192.0)); + // 9:59 -> 15:59 + await tester.drag(find.text('9'), _kRowOffset * 6); await tester.pump(); await tester.pump(const Duration(milliseconds: 500));