From b68e166456749167d44aacdfdda202c520e0511b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 May 2021 14:13:41 +0200 Subject: [PATCH 1/2] bugfix/ fixed range of default dates for the datepicker --- example/lib/main.dart | 2 +- lib/src/answer_format/date_answer_format.dart | 10 ++- lib/src/views/date_answer_view.dart | 38 +++++----- test/date_answer_view_test.dart | 70 +++++++++++++++++++ 4 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 test/date_answer_view_test.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index ed78685a..d67b0329 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -172,8 +172,8 @@ class _MyAppState extends State { title: 'When was your last holiday?', answerFormat: DateAnswerFormat( minDate: DateTime.utc(1970), - maxDate: DateTime.now(), defaultDate: DateTime.now(), + maxDate: DateTime.now(), ), ), CompletionStep( diff --git a/lib/src/answer_format/date_answer_format.dart b/lib/src/answer_format/date_answer_format.dart index c69374db..8f4d3fd7 100644 --- a/lib/src/answer_format/date_answer_format.dart +++ b/lib/src/answer_format/date_answer_format.dart @@ -9,5 +9,13 @@ class DateAnswerFormat implements AnswerFormat { this.defaultDate, this.minDate, this.maxDate, - }); + }) : assert(minDate == null || maxDate == null || minDate.isBefore(maxDate)), + assert(defaultDate == null || + minDate == null || + defaultDate.isAtSameMomentAs(minDate) || + defaultDate.isAfter(minDate)), + assert(defaultDate == null || + maxDate == null || + defaultDate.isAtSameMomentAs(maxDate) || + defaultDate.isBefore(maxDate)); } diff --git a/lib/src/views/date_answer_view.dart b/lib/src/views/date_answer_view.dart index d470462e..65405031 100644 --- a/lib/src/views/date_answer_view.dart +++ b/lib/src/views/date_answer_view.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -27,14 +25,16 @@ class DateAnswerView extends StatefulWidget { class _DateAnswerViewState extends State { final DateFormat _dateFormat = DateFormat('E, MMM d'); final DateTime _startDate = DateTime.now(); - late final DateAnswerFormat _dateAnswerFormat; + late DateAnswerFormat _dateAnswerFormat; DateTime? _result; @override void initState() { super.initState(); _dateAnswerFormat = widget.questionStep.answerFormat as DateAnswerFormat; - _result = widget.result?.result ?? DateTime.now(); + _result = widget.result?.result ?? + _dateAnswerFormat.defaultDate ?? + DateTime.now(); } void _handleDateChanged(DateTime date) { @@ -43,6 +43,7 @@ class _DateAnswerViewState extends State { @override Widget build(BuildContext context) { + final platform = Theme.of(context).platform; return StepView( step: widget.questionStep, controller: SurveyController( @@ -73,7 +74,9 @@ class _DateAnswerViewState extends State { textAlign: TextAlign.center, ), ), - Platform.isAndroid ? _androidDatePicker() : _iosDatePicker(), + platform == TargetPlatform.iOS + ? _iosDatePicker() + : _androidDatePicker(), ], ), ); @@ -96,7 +99,7 @@ class _DateAnswerViewState extends State { left: 8.0, bottom: 8.0, child: Text( - _result != null ? _dateFormat.format(_result!) : '', + _dateFormat.format(_result!), style: TextStyle( fontSize: 28.0, color: Colors.white, @@ -110,14 +113,14 @@ class _DateAnswerViewState extends State { width: double.infinity, height: 300.0, child: CalendarDatePicker( - firstDate: _dateAnswerFormat.minDate ?? - DateTime.now().add(Duration(days: 365 * DateTime.now().year)), - lastDate: _dateAnswerFormat.maxDate ?? - DateTime.now().add(Duration(days: 365 * 100)), - initialDate: _result ?? - _dateAnswerFormat.defaultDate ?? - _dateAnswerFormat.maxDate ?? - DateTime.now(), + firstDate: _dateAnswerFormat.minDate ?? DateTime.utc(1900), + lastDate: _dateAnswerFormat.maxDate?.add( + Duration(hours: 1), + ) ?? + DateTime.now().add( + Duration(hours: 1), + ), + initialDate: _result ?? DateTime.now(), currentDate: _result, onDateChanged: (DateTime value) => _handleDateChanged(value), ), @@ -135,8 +138,11 @@ class _DateAnswerViewState extends State { minimumDate: _dateAnswerFormat.minDate, //We have to add an hour to to met the assert maxDate > initDate maximumDate: _dateAnswerFormat.maxDate?.add( - Duration(hours: 1), - ), + Duration(hours: 1), + ) ?? + DateTime.now().add( + Duration(hours: 1), + ), initialDateTime: _dateAnswerFormat.defaultDate, onDateTimeChanged: (DateTime value) { setState(() { diff --git a/test/date_answer_view_test.dart b/test/date_answer_view_test.dart new file mode 100644 index 00000000..283c6845 --- /dev/null +++ b/test/date_answer_view_test.dart @@ -0,0 +1,70 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:survey_kit/src/answer_format/date_answer_format.dart'; +import 'package:survey_kit/src/steps/predefined_steps/question_step.dart'; +import 'package:survey_kit/src/views/date_answer_view.dart'; + +void main() { + DateAnswerView _validDateAnswerView() => DateAnswerView( + questionStep: QuestionStep( + answerFormat: DateAnswerFormat( + minDate: DateTime.now().subtract(const Duration(days: 365 * 70)), + maxDate: DateTime.now().subtract(const Duration(days: 365 * 15)), + defaultDate: + DateTime.now().subtract(const Duration(days: 365 * 20)), + ), + ), + result: null, + ); + + testWidgets('Detects iOS platform and displays correct widget', + (WidgetTester tester) async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + await tester.pumpWidget( + CupertinoApp( + home: _validDateAnswerView(), + ), + ); + await tester.pumpAndSettle(); + expect(find.byType(CupertinoDatePicker), findsOneWidget); + debugDefaultTargetPlatformOverride = null; + }); + + testWidgets('Detects Android platform and displays correct widget', + (WidgetTester tester) async { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: _validDateAnswerView(), + ), + ), + ); + await tester.pumpAndSettle(); + expect(find.byType(CalendarDatePicker), findsOneWidget); + debugDefaultTargetPlatformOverride = null; + }); + + testWidgets('Initial date in between first and last date', + (WidgetTester tester) async { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: DateAnswerView( + questionStep: QuestionStep( + title: 'Your Birthday?', + answerFormat: DateAnswerFormat(), + ), + result: null, + ), + ), + ), + ); + await tester.pumpAndSettle(); + expect(find.byType(CalendarDatePicker), findsOneWidget); + debugDefaultTargetPlatformOverride = null; + }); +} From 0ed688ded5bac061e163b6fa6c41949d10e8a577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20M=C3=A4rz?= Date: Thu, 6 May 2021 14:26:40 +0200 Subject: [PATCH 2/2] Added documentation to date answer views --- lib/src/answer_format/date_answer_format.dart | 5 +++++ lib/src/views/date_answer_view.dart | 3 +++ 2 files changed, 8 insertions(+) diff --git a/lib/src/answer_format/date_answer_format.dart b/lib/src/answer_format/date_answer_format.dart index 8f4d3fd7..82223698 100644 --- a/lib/src/answer_format/date_answer_format.dart +++ b/lib/src/answer_format/date_answer_format.dart @@ -1,8 +1,13 @@ import 'package:survey_kit/src/answer_format/answer_format.dart'; class DateAnswerFormat implements AnswerFormat { + /// Default date which will be preselected on datepicker opening final DateTime? defaultDate; + + /// Lowest date which can be selected via the datepicker final DateTime? minDate; + + /// Highest date which can be selected via the datepicker final DateTime? maxDate; DateAnswerFormat({ diff --git a/lib/src/views/date_answer_view.dart b/lib/src/views/date_answer_view.dart index 65405031..c274748e 100644 --- a/lib/src/views/date_answer_view.dart +++ b/lib/src/views/date_answer_view.dart @@ -9,7 +9,10 @@ import 'package:survey_kit/src/steps/predefined_steps/question_step.dart'; import 'package:survey_kit/src/views/widget/step_view.dart'; class DateAnswerView extends StatefulWidget { + /// [QuestionStep] which includes the [DateAnswerFormat] final QuestionStep questionStep; + + /// [DateQuestionResult] which boxes the result final DateQuestionResult? result; const DateAnswerView({