diff --git a/CHANGELOG.md b/CHANGELOG.md index 70a6ea9..71e0586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ -## 0.0.1 - September 29th 2022 +## 0.0.2 - October 10th 2022 - Initial release -## 0.0.2 - October 12th 2022 +## 0.1.0 - October 12th 2022 - Added a multi line plain text input widget - Ability to set the scrolldirection of the pageview - Ability to set the scrollphysics of the pages' scrollview. + +## 0.2.0 - October 13th 2022 + +- Added date input widget diff --git a/example/lib/example_pages/age_page.dart b/example/lib/example_pages/age_page.dart index 83d6c93..66f0f92 100644 --- a/example/lib/example_pages/age_page.dart +++ b/example/lib/example_pages/age_page.dart @@ -25,7 +25,7 @@ class _AgePageState extends State { fontSize: fontSize, title: "What is your age?", pageNumber: 1, - amountOfPages: 3, + amountOfPages: 4, flutterFormWidgets: [ FlutterFormInputNumberPicker( minValue: 12, diff --git a/example/lib/example_pages/carousel_page.dart b/example/lib/example_pages/carousel_page.dart index ac763c4..01d567b 100644 --- a/example/lib/example_pages/carousel_page.dart +++ b/example/lib/example_pages/carousel_page.dart @@ -27,7 +27,7 @@ class _CarouselPageState extends State { fontSize: fontSize, title: "What's your favorite car?", pageNumber: 3, - amountOfPages: 3, + amountOfPages: 4, flutterFormWidgets: [ FlutterFormInputCarousel( controller: widget.inputController, items: getCars()) diff --git a/example/lib/example_pages/date_page.dart b/example/lib/example_pages/date_page.dart new file mode 100644 index 0000000..169e946 --- /dev/null +++ b/example/lib/example_pages/date_page.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form/flutter_form.dart'; +import 'package:form_example/template_page.dart'; +import 'package:intl/intl.dart'; + +class DatePage extends StatefulWidget { + const DatePage({ + required this.dateController, + super.key, + }); + + final FlutterFormInputPlainTextController dateController; + + @override + State createState() => _DatePageState(); +} + +class _DatePageState extends State { + @override + Widget build(BuildContext context) { + var size = MediaQuery.of(context).size; + var fontSize = size.height / 40; + + return TemplatePage( + size: size, + fontSize: fontSize, + pageNumber: 4, + amountOfPages: 4, + title: "Please enter a date", + flutterFormWidgets: [ + Padding( + padding: const EdgeInsets.fromLTRB(40, 0, 40, 40), + child: FlutterFormInputDateTime( + inputType: FlutterFormDateTimeType.dateTime, + dateFormat: DateFormat.yMd(), + firstDate: DateTime.now(), + label: const Text("Date"), + controller: widget.dateController, + ), + ), + ], + ); + } +} diff --git a/example/lib/example_pages/name_page.dart b/example/lib/example_pages/name_page.dart index 0b8edcf..d3e2772 100644 --- a/example/lib/example_pages/name_page.dart +++ b/example/lib/example_pages/name_page.dart @@ -28,7 +28,7 @@ class _NamePageState extends State { size: size, fontSize: fontSize, pageNumber: 2, - amountOfPages: 3, + amountOfPages: 4, title: "Please enter your name", flutterFormWidgets: [ Padding( diff --git a/example/lib/form_example.dart b/example/lib/form_example.dart index c3b0c88..9dbe0ea 100644 --- a/example/lib/form_example.dart +++ b/example/lib/form_example.dart @@ -6,6 +6,8 @@ import 'package:form_example/example_pages/carousel_page.dart'; import 'package:form_example/example_pages/check_page.dart'; import 'package:form_example/example_pages/name_page.dart'; +import 'example_pages/date_page.dart'; + class FormExample extends ConsumerStatefulWidget { const FormExample({Key? key}) : super(key: key); @@ -63,6 +65,15 @@ class _FormExampleState extends ConsumerState { onChanged: (value) => debugPrint(value), ); + FlutterFormInputPlainTextController dateController = + FlutterFormInputPlainTextController( + mandatory: true, + id: "date", + checkPageTitle: (dynamic date) { + return "Date: $date"; + }, + ); + @override void initState() { super.initState(); @@ -193,6 +204,11 @@ class _FormExampleState extends ConsumerState { cars: cars, ), ), + FlutterFormPage( + child: DatePage( + dateController: dateController, + ), + ), ], checkPage: CheckPageExample() .showCheckpage(context, size, fontSize, checkPageText), diff --git a/example/pubspec.lock b/example/pubspec.lock index 864d646..bb95e48 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -87,7 +87,7 @@ packages: source: sdk version: "0.0.0" intl: - dependency: transitive + dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index c13fe9e..2d60953 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: flutter_riverpod: ^1.0.4 flutter_form: path: ../ + intl: ^0.17.0 dev_dependencies: diff --git a/lib/src/widgets/input/input_types/input_date_picker/date_picker.dart b/lib/src/widgets/input/input_types/input_date_picker/date_picker.dart new file mode 100644 index 0000000..f651353 --- /dev/null +++ b/lib/src/widgets/input/input_types/input_date_picker/date_picker.dart @@ -0,0 +1,129 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_form/utils/translation_service.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; +import '../../../../../flutter_form.dart'; + +/// Generates a [DateTimeInputField] for DateTimes/Dates/Times/DateRanges. +/// It requires a [FlutterFormInputController], [inputType], [dateFormat], [firstDate], and [lastDate] +class DateTimeInputField extends ConsumerStatefulWidget { + const DateTimeInputField({ + Key? key, + required this.inputType, + required this.controller, + this.label, + this.showIcon = true, + this.icon = Icons.calendar_today, + required this.dateFormat, + required this.firstDate, + required this.lastDate, + }) : super( + key: key, + ); + final FlutterFormDateTimeType inputType; + final FlutterFormInputController controller; + final DateFormat dateFormat; + final bool showIcon; + final DateTime? firstDate; + final DateTime? lastDate; + final IconData icon; + final Widget? label; + @override + ConsumerState createState() => _DateInputFieldState(); +} + +class _DateInputFieldState extends ConsumerState { + late final DateTime firstDate; + late final DateTime lastDate; + + @override + void initState() { + firstDate = widget.firstDate ?? + DateTime.now().subtract( + const Duration(days: 1000), + ); + lastDate = widget.lastDate ?? + DateTime.now().add( + const Duration(days: 1000), + ); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + String Function(String, {List? params}) _ = + getTranslator(context, ref); + + Future getInputFromUser(FlutterFormDateTimeType inputType) async { + String userInput = ''; + switch (inputType) { + case FlutterFormDateTimeType.date: + DateTime? unformatted = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: firstDate, + lastDate: lastDate, + ); + userInput = unformatted != null + ? widget.dateFormat.format(unformatted) + : userInput; + break; + case FlutterFormDateTimeType.dateTime: + await getInputFromUser(FlutterFormDateTimeType.date) + .then((value) async { + if (value != '') { + String secondInput = + await getInputFromUser(FlutterFormDateTimeType.time); + if (secondInput != '') { + userInput = '$value $secondInput'; + } + } + }); + break; + case FlutterFormDateTimeType.range: + userInput = (await showDateRangePicker( + context: context, + firstDate: firstDate, + lastDate: lastDate, + ).then((value) { + return value != null + ? '${widget.dateFormat.format(value.start)} - ${widget.dateFormat.format(value.end)}' + : ''; + })) + .toString(); + break; + case FlutterFormDateTimeType.time: + userInput = await showTimePicker( + context: context, initialTime: TimeOfDay.now()) + .then((value) => value == null ? '' : value.format(context)); + } + return userInput; + } + + return TextFormField( + keyboardType: TextInputType.none, + readOnly: true, + key: Key(widget.controller.value.toString()), + initialValue: widget.controller.value, + onSaved: (value) { + widget.controller.onSaved(value); + }, + onTap: () async { + String userInput = await getInputFromUser(widget.inputType); + setState(() { + widget.controller.value = + userInput != '' ? userInput : widget.controller.value; + }); + }, + validator: (value) => widget.controller.onValidate(value, _), + decoration: InputDecoration( + suffixIcon: widget.showIcon ? Icon(widget.icon) : null, + focusColor: Theme.of(context).primaryColor, + label: widget.label ?? const Text("Date"), + ), + ); + } +} diff --git a/lib/src/widgets/input/input_types/input_date_picker/input_date_picker.dart b/lib/src/widgets/input/input_types/input_date_picker/input_date_picker.dart new file mode 100644 index 0000000..8ad96a9 --- /dev/null +++ b/lib/src/widgets/input/input_types/input_date_picker/input_date_picker.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form/src/widgets/input/input_types/input_date_picker/date_picker.dart'; +import 'package:flutter_form/utils/translation_service.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../../../../../flutter_form.dart'; + +/// Select Input Types in a [FlutterFormInputDateTime] +enum FlutterFormDateTimeType { + date, + time, + dateTime, + range, +} + +/// Input for a dateTime used in a [FlutterForm]. +/// +/// Standard controller is [FlutterFormInputDateController]. +class FlutterFormInputDateTime extends FlutterFormInputWidget { + const FlutterFormInputDateTime({ + Key? key, + required FlutterFormInputController controller, + Widget? label, + this.showIcon = true, + required this.inputType, + required this.dateFormat, + this.firstDate, + this.lastDate, + this.icon = Icons.calendar_today, + }) : super( + key: key, + controller: controller, + label: label, + ); + final bool showIcon; + final FlutterFormDateTimeType inputType; + final DateFormat dateFormat; + final DateTime? firstDate; + final DateTime? lastDate; + final IconData icon; + + @override + Widget build(BuildContext context, WidgetRef ref) { + String Function(String, {List? params}) _ = + getTranslator(context, ref); + super.registerController(context); + + return DateTimeInputField( + firstDate: firstDate, + lastDate: lastDate, + inputType: inputType, + controller: controller, + dateFormat: dateFormat, + ); + } +} + +/// Controller for dates used by a [FlutterFormInputWidget] used in a [FlutterForm]. +/// +/// Mainly used by [FlutterFormInputDateTime]. +class FlutterFormInputDateTimeController + implements FlutterFormInputController { + FlutterFormInputDateTimeController({ + required this.id, + this.mandatory = true, + this.value, + this.checkPageTitle, + this.checkPageDescription, + required this.dateTimeType, + required this.dateFormat, + }); + + final DateFormat dateFormat; + final FlutterFormDateTimeType dateTimeType; + + @override + String? id; + + @override + String? value; + + @override + bool mandatory; + + @override + String Function(String? value)? checkPageTitle; + + @override + String Function(String? value)? checkPageDescription; + + @override + void Function(String? value)? onChanged; + + @override + void onSaved(dynamic value) { + this.value = value; + } + + @override + String? onValidate(String? value, + String Function(String, {List? params}) translator) { + if (mandatory) { + if (value == null || value.isEmpty) { + return translator('shell.form.error.empty'); + } + } + + return null; + } +} diff --git a/lib/src/widgets/input/input_types/input_types.dart b/lib/src/widgets/input/input_types/input_types.dart index 2d95e4c..7b4053e 100644 --- a/lib/src/widgets/input/input_types/input_types.dart +++ b/lib/src/widgets/input/input_types/input_types.dart @@ -5,3 +5,4 @@ export 'input_password/input_password.dart'; export 'input_plain_text.dart'; export 'input_slider/input_slider.dart'; export 'input_switch/input_switch.dart'; +export 'input_date_picker/input_date_picker.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index d148513..e1f3b0e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ homepage: publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: ">=2.18.0 <3.0.0" flutter: ">=1.17.0" dependencies: @@ -15,6 +15,7 @@ dependencies: flutter_localizations: sdk: flutter flutter_riverpod: ^1.0.4 + intl: ^0.17.0 localization: ^2.1.0 dev_dependencies: