diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af7bbf..c1c3ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,4 +128,9 @@ ## 6.3.0 - February 21th 2024 - Updated the `flutter_input_library` from 3.1.0 to 3.2.1 - Added `FlutterFormInputPhone` for phone number with dial code selection. -- Added `InputDecoration` parameter to the following inputfields: `FlutterFormInputEmail` and `FlutterFormInputPassword` \ No newline at end of file +- Added `InputDecoration` parameter to the following inputfields: `FlutterFormInputEmail` and `FlutterFormInputPassword` + +## 6.3.1 - February 29th 2024 +- Removed `TranslationService` and add `validationMessage` property to fields with validation. +- Added a way to override a input field validator. +- Exposed all properties for `FlutterFormInputDateTime` provided by `flutter_input_library`. \ No newline at end of file diff --git a/example/lib/example_pages/age_page.dart b/example/lib/example_pages/age_page.dart index 34ee552..e2a23ac 100644 --- a/example/lib/example_pages/age_page.dart +++ b/example/lib/example_pages/age_page.dart @@ -35,6 +35,7 @@ class _AgePageState extends State { minValue: 12, maxValue: 120, controller: widget.inputController, + validationMessage: 'Please fill in your age.', ), ], ); diff --git a/example/lib/example_pages/carousel_page.dart b/example/lib/example_pages/carousel_page.dart index f73cd4a..e2927df 100644 --- a/example/lib/example_pages/carousel_page.dart +++ b/example/lib/example_pages/carousel_page.dart @@ -34,7 +34,10 @@ class _CarouselPageState extends State { amountOfPages: 4, flutterFormWidgets: [ FlutterFormInputCarousel( - controller: widget.inputController, items: getCars()) + controller: widget.inputController, + items: getCars(), + validationMessage: 'Validation error message.', + ) ], ); } diff --git a/example/lib/example_pages/date_page.dart b/example/lib/example_pages/date_page.dart index 829b7e9..4f9fc81 100644 --- a/example/lib/example_pages/date_page.dart +++ b/example/lib/example_pages/date_page.dart @@ -46,6 +46,7 @@ class _DatePageState extends State { ), label: const Text("Custom date label"), controller: widget.dateController, + validationMessage: 'Please fill in a date.', ), ), ], diff --git a/example/lib/example_pages/name_page.dart b/example/lib/example_pages/name_page.dart index a70eef6..d426b6c 100644 --- a/example/lib/example_pages/name_page.dart +++ b/example/lib/example_pages/name_page.dart @@ -40,6 +40,7 @@ class _NamePageState extends State { child: FlutterFormInputPlainText( label: const Text("First Name"), controller: widget.firstNameController, + validationMessage: 'Please fill in your first name.', ), ), if (widget.showLastName) @@ -48,6 +49,7 @@ class _NamePageState extends State { child: FlutterFormInputPlainText( label: const Text("Last Name"), controller: widget.lastNameController, + validationMessage: 'Please fill in your last name.', ), ), ], diff --git a/example/lib/form_example.dart b/example/lib/form_example.dart index 2d44693..515f624 100644 --- a/example/lib/form_example.dart +++ b/example/lib/form_example.dart @@ -189,6 +189,7 @@ class _FormExampleState extends ConsumerState { child: Center( child: FlutterFormInputPhone( controller: phoneInputController, + validationMessage: 'Please fill in a valid phone number', ), ), ), diff --git a/lib/flutter_form.dart b/lib/flutter_form.dart index 87b04f6..dae85a3 100644 --- a/lib/flutter_form.dart +++ b/lib/flutter_form.dart @@ -9,4 +9,3 @@ export 'src/form.dart'; export 'src/widgets/input/abstractions.dart'; export 'src/widgets/input/input_types/input_types.dart'; export 'utils/form.dart'; -export 'utils/translation_service.dart'; diff --git a/lib/src/widgets/input/abstractions.dart b/lib/src/widgets/input/abstractions.dart index 4846eab..3aa9254 100644 --- a/lib/src/widgets/input/abstractions.dart +++ b/lib/src/widgets/input/abstractions.dart @@ -122,6 +122,6 @@ abstract class FlutterFormInputController { /// [onValidate] is used to validate the given input by the user. String? onValidate( T? value, - String Function(String, {List? params}) translator, + String validationMessage, ); } diff --git a/lib/src/widgets/input/input_types/input_carousel/input_carousel.dart b/lib/src/widgets/input/input_types/input_carousel/input_carousel.dart index 767c7cb..bf91797 100644 --- a/lib/src/widgets/input/input_types/input_carousel/input_carousel.dart +++ b/lib/src/widgets/input/input_types/input_carousel/input_carousel.dart @@ -16,24 +16,27 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input; class FlutterFormInputCarousel extends FlutterFormInputWidget { const FlutterFormInputCarousel({ required super.controller, + required this.validationMessage, required this.items, super.key, super.label, + this.validator, this.height = 425, }); final List items; final double height; + final String validationMessage; + final String? Function(int?)? validator; @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); return input.FlutterFormInputCarousel( onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), onChanged: controller.onChanged, initialValue: controller.value ?? 0, items: items, @@ -86,7 +89,7 @@ class FlutterFormInputCarouselController @override String? onValidate( int? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) {} 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 index 3644643..a989473 100644 --- 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 @@ -33,36 +33,57 @@ class FlutterFormInputDateTime extends FlutterFormInputWidget { required super.controller, required this.inputType, required this.dateFormat, + required this.validationMessage, + this.decoration, + this.style, super.key, - super.label, + this.label, this.showIcon = true, this.firstDate, this.lastDate, this.initialDate, this.initialDateTimeRange, + this.initialTime, this.icon = Icons.calendar_today, + this.initialValue, + this.onChanged, + this.onSaved, + this.validator, + this.autovalidateMode = AutovalidateMode.disabled, + this.timePickerEntryMode = TimePickerEntryMode.dial, this.enabled = true, this.onTapEnabled = true, }); + final TextStyle? style; + final InputDecoration? decoration; + final Widget? label; final bool showIcon; final input.FlutterFormDateTimeType inputType; final DateFormat dateFormat; final DateTime? initialDate; final DateTimeRange? initialDateTimeRange; + final TimeOfDay? initialTime; final DateTime? firstDate; final DateTime? lastDate; final IconData icon; + final String? initialValue; + final String? Function(String?)? validator; + final String validationMessage; + final void Function(String?)? onSaved; + final void Function(String?)? onChanged; + final AutovalidateMode autovalidateMode; + final TimePickerEntryMode timePickerEntryMode; final bool enabled; final bool onTapEnabled; @override Widget build(BuildContext context) { - var translator = getTranslator(context); super.registerController(context); return input.FlutterFormInputDateTime( enabled: enabled, showIcon: showIcon, + decoration: decoration, onTapEnabled: onTapEnabled, label: label, icon: icon, @@ -71,7 +92,8 @@ class FlutterFormInputDateTime extends FlutterFormInputWidget { inputType: inputType, onChanged: (value) => controller.onChanged?.call(value), onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), initialValue: controller.value, dateFormat: dateFormat, initialDate: initialDate, @@ -143,11 +165,11 @@ class FlutterFormInputDateTimeController @override String? onValidate( String? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) { if (value == null || value.isEmpty) { - return translator('shell.form.error.empty'); + return validationMessage; } } diff --git a/lib/src/widgets/input/input_types/input_email.dart b/lib/src/widgets/input/input_types/input_email.dart index dfd3422..87a6e11 100644 --- a/lib/src/widgets/input/input_types/input_email.dart +++ b/lib/src/widgets/input/input_types/input_email.dart @@ -16,21 +16,23 @@ class FlutterFormInputEmail extends FlutterFormInputWidget { /// The [key], [focusNode], [label], and [enabled] parameters are optional. const FlutterFormInputEmail({ required super.controller, + required this.validationMessage, super.key, super.focusNode, super.label, bool? enabled, + this.validator, this.decoration, }) : super( enabled: enabled ?? true, ); final InputDecoration? decoration; + final String validationMessage; + final String? Function(String?)? validator; @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); return input.FlutterFormInputPlainText( @@ -40,7 +42,8 @@ class FlutterFormInputEmail extends FlutterFormInputWidget { controller.onSaved(value); }, focusNode: focusNode, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), onChanged: (value) => controller.onChanged?.call(value), decoration: decoration, ); @@ -98,17 +101,17 @@ class FlutterFormInputEmailController @override String? onValidate( String? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) { if (value == null || value.isEmpty) { - return translator('shell.form.error.empty'); + return validationMessage; } if (!RegExp( r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+", ).hasMatch(value)) { - return translator('shell.form.error.email.notValid'); + return validationMessage; } } diff --git a/lib/src/widgets/input/input_types/input_number_picker/input_number_picker.dart b/lib/src/widgets/input/input_types/input_number_picker/input_number_picker.dart index 3cbcf47..a104968 100644 --- a/lib/src/widgets/input/input_types/input_number_picker/input_number_picker.dart +++ b/lib/src/widgets/input/input_types/input_number_picker/input_number_picker.dart @@ -15,26 +15,29 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input; class FlutterFormInputNumberPicker extends FlutterFormInputWidget { const FlutterFormInputNumberPicker({ required super.controller, + required this.validationMessage, super.key, super.label, + this.validator, this.minValue = 0, this.maxValue = 100, }) : assert(minValue < maxValue, 'minValue must be less than maxValue'); final int minValue; final int maxValue; + final String validationMessage; + final String? Function(int?)? validator; @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); return input.FlutterFormInputNumberPicker( minValue: minValue, maxValue: maxValue, onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), onChanged: (value) => controller.onChanged?.call(value), initialValue: controller.value ?? minValue, ); @@ -91,7 +94,7 @@ class FlutterFormInputNumberPickerController @override String? onValidate( int? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) {} diff --git a/lib/src/widgets/input/input_types/input_password/input_password.dart b/lib/src/widgets/input/input_types/input_password/input_password.dart index d3cf821..b60832e 100644 --- a/lib/src/widgets/input/input_types/input_password/input_password.dart +++ b/lib/src/widgets/input/input_types/input_password/input_password.dart @@ -12,29 +12,31 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input; class FlutterFormInputPassword extends FlutterFormInputWidget { const FlutterFormInputPassword({ required super.controller, + required this.validationMessage, super.key, super.focusNode, super.label, bool? enabled, + this.validator, this.decoration, }) : super( enabled: enabled ?? true, ); final InputDecoration? decoration; + final String validationMessage; + final String? Function(String?)? validator; @override Widget build(BuildContext context) { super.registerController(context); - - var translator = getTranslator(context); - return input.FlutterFormInputPassword( enabled: enabled, initialValue: controller.value, focusNode: focusNode, onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), onChanged: (value) => controller.onChanged?.call(value), onFieldSubmitted: (value) => controller.onSubmit?.call(value), decoration: decoration, @@ -93,15 +95,15 @@ class FlutterFormInputPasswordController @override String? onValidate( String? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) { if (value == null || value.isEmpty) { - return translator('Field can not be empty'); + return validationMessage; } if (value.length < 6) { - return translator('Field should be at least 6 characters long'); + return validationMessage; } } diff --git a/lib/src/widgets/input/input_types/input_phone.dart b/lib/src/widgets/input/input_types/input_phone.dart index 24afbe1..9f46dec 100644 --- a/lib/src/widgets/input/input_types/input_phone.dart +++ b/lib/src/widgets/input/input_types/input_phone.dart @@ -14,12 +14,14 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input; class FlutterFormInputPhone extends FlutterFormInputWidget { const FlutterFormInputPhone({ required super.controller, + required this.validationMessage, super.key, super.focusNode, super.label, this.decoration, this.enabled = true, this.numberFieldStyle, + this.validator, this.dialCodeSelectorStyle, this.dialCodeSelectorPadding = const EdgeInsets.only(top: 6), this.textAlignVertical = TextAlignVertical.top, @@ -32,11 +34,11 @@ class FlutterFormInputPhone extends FlutterFormInputWidget { final TextStyle? dialCodeSelectorStyle; final EdgeInsets dialCodeSelectorPadding; final TextAlignVertical textAlignVertical; + final String validationMessage; + final String? Function(PhoneNumber?)? validator; @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); var inputDecoration = decoration ?? @@ -50,7 +52,8 @@ class FlutterFormInputPhone extends FlutterFormInputWidget { enabled: enabled, initialValue: controller.value, onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), onChanged: (value) => controller.onChanged?.call(value), onFieldSubmitted: (value) => controller.onSubmit?.call(value), decoration: inputDecoration, @@ -100,11 +103,11 @@ class FlutterFormInputPhoneController @override String? onValidate( input.PhoneNumber? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) { if (value == null || value.number == null) { - return translator('Field can not be empty'); + return validationMessage; } } diff --git a/lib/src/widgets/input/input_types/input_plain_text.dart b/lib/src/widgets/input/input_types/input_plain_text.dart index 1050004..bafdfbd 100644 --- a/lib/src/widgets/input/input_types/input_plain_text.dart +++ b/lib/src/widgets/input/input_types/input_plain_text.dart @@ -20,6 +20,7 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget { /// [enabled], [style], and [textCapitalization] parameters are optional. const FlutterFormInputPlainText({ required super.controller, + required this.validationMessage, super.key, super.focusNode, super.label, @@ -30,6 +31,7 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget { this.scrollPadding, this.maxLength, this.keyboardType, + this.validator, this.enabled = true, this.style, this.textCapitalization = TextCapitalization.none, @@ -46,11 +48,11 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget { final bool enabled; final TextStyle? style; final TextCapitalization textCapitalization; + final String validationMessage; + final String? Function(String?)? validator; @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); var inputDecoration = decoration ?? @@ -65,7 +67,8 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget { initialValue: controller.value, focusNode: focusNode, onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), onChanged: (value) => controller.onChanged?.call(value), onFieldSubmitted: (value) => controller.onSubmit?.call(value), decoration: inputDecoration, @@ -89,10 +92,12 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget { class FlutterFormInputMultiLine extends StatelessWidget { const FlutterFormInputMultiLine({ required this.controller, + required this.validationMessage, super.key, this.focusNode, this.label, this.hint, + this.validator, this.maxCharacters, this.enabled = true, this.textCapitalization = TextCapitalization.sentences, @@ -119,23 +124,24 @@ class FlutterFormInputMultiLine extends StatelessWidget { /// The capitalization behavior for the input field. final TextCapitalization textCapitalization; - @override - Widget build(BuildContext context) { - var translator = getTranslator(context); + final String validationMessage; - return input.FlutterFormInputMultiLine( - enabled: enabled, - label: label, - hint: hint, - focusNode: focusNode, - initialValue: controller.value, - maxCharacters: maxCharacters, - onChanged: controller.onChanged, - onSaved: controller.onSaved, - validator: (v) => controller.onValidate(v, translator), - textCapitalization: textCapitalization, - ); - } + final String? Function(String?)? validator; + + @override + Widget build(BuildContext context) => input.FlutterFormInputMultiLine( + enabled: enabled, + label: label, + hint: hint, + focusNode: focusNode, + initialValue: controller.value, + maxCharacters: maxCharacters, + onChanged: controller.onChanged, + onSaved: controller.onSaved, + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), + textCapitalization: textCapitalization, + ); } /// Controller for plain text used by a [FlutterFormInputWidget] used in a @@ -189,11 +195,11 @@ class FlutterFormInputPlainTextController @override String? onValidate( String? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) { if (value == null || value.isEmpty) { - return translator('Field can not be empty'); + return validationMessage; } } diff --git a/lib/src/widgets/input/input_types/input_slider/input_slider.dart b/lib/src/widgets/input/input_types/input_slider/input_slider.dart index 1277a79..4fc2dd9 100644 --- a/lib/src/widgets/input/input_types/input_slider/input_slider.dart +++ b/lib/src/widgets/input/input_types/input_slider/input_slider.dart @@ -19,26 +19,29 @@ class FlutterFormInputSlider extends FlutterFormInputWidget { /// The [enabled] parameter specifies whether the input field is enabled. const FlutterFormInputSlider({ required super.controller, + required this.validationMessage, super.key, super.focusNode, super.label, + this.validator, this.minValue = 0, this.maxValue = 100, }) : assert(minValue < maxValue, 'minValue must be less than maxValue'); final int minValue; final int maxValue; + final String validationMessage; + final String? Function(double?)? validator; @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); return input.FlutterFormInputSlider( focusNode: focusNode, onSaved: controller.onSaved, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), ); } } @@ -93,7 +96,7 @@ class FlutterFormInputSliderController @override String? onValidate( double? value, - String Function(String, {List? params}) translator, + String validationMessage, ) { if (mandatory) {} diff --git a/lib/src/widgets/input/input_types/input_switch/input_switch.dart b/lib/src/widgets/input/input_types/input_switch/input_switch.dart index bc7eabe..b6399fe 100644 --- a/lib/src/widgets/input/input_types/input_switch/input_switch.dart +++ b/lib/src/widgets/input/input_types/input_switch/input_switch.dart @@ -14,22 +14,26 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input; class FlutterFormInputSwitch extends FlutterFormInputWidget { const FlutterFormInputSwitch({ required super.controller, + required this.validationMessage, super.key, super.focusNode, super.label, + this.validator, }); + final String validationMessage; + final String? Function(bool?)? validator; + @override Widget build(BuildContext context) { - var translator = getTranslator(context); - super.registerController(context); return input.FlutterFormInputBool( focusNode: focusNode, onSaved: controller.onSaved, onChanged: controller.onChanged, - validator: (value) => controller.onValidate(value, translator), + validator: validator ?? + (value) => controller.onValidate(value, validationMessage), initialValue: controller.value ?? false, widgetType: input.BoolWidgetType.switchWidget, ); @@ -86,7 +90,7 @@ class FlutterFormInputSwitchController @override String? onValidate( bool? value, - String Function(String, {List? params}) translator, + String validationMessage, ) => null; } diff --git a/lib/utils/translation_service.dart b/lib/utils/translation_service.dart deleted file mode 100644 index e911aea..0000000 --- a/lib/utils/translation_service.dart +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Iconica -// -// SPDX-License-Identifier: BSD-3-Clause - -import 'package:flutter/material.dart'; - -abstract class TranslationService { - TranslationService._(); - - String translate( - BuildContext context, - String key, { - List? params, - }); - - String number(double value); -} - -typedef Translator = String Function( - String, { - List? params, -}); - -class ShellTranslationService implements TranslationService { - @override - String number(double value) => value.toStringAsFixed(2); - - @override - String translate(BuildContext context, String key, {List? params}) => - key; -} - -Translator getTranslator(BuildContext context) { - try { - var translator = ShellTranslationService().translate; - return ( - String key, { - List? params, - }) => - translator(context, key, params: params); - } on Exception catch (_) { - return ( - String key, { - List? params, - }) => - key; - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 3d29b45..034904b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_form_wizard description: A new Flutter package project. -version: 6.3.0 +version: 6.3.1 homepage: https://github.com/Iconica-Development/flutter_form_wizard publish_to: none diff --git a/test/flutter_form_test.dart b/test/flutter_form_test.dart index ff18276..16528f9 100644 --- a/test/flutter_form_test.dart +++ b/test/flutter_form_test.dart @@ -56,6 +56,7 @@ void main() { child: FlutterFormInputPlainText( label: const Text('Field1Label'), controller: testField1Controller, + validationMessage: 'Please fill in this field', ), ), ), @@ -64,6 +65,7 @@ void main() { child: FlutterFormInputPlainText( label: const Text('Field2Label'), controller: testField2Controller, + validationMessage: 'Please fill in this field', ), ), ), @@ -154,6 +156,7 @@ void main() { child: FlutterFormInputPlainText( label: const Text('Field1Label'), controller: testField1Controller, + validationMessage: 'Please fill in this field', ), ), ), @@ -162,6 +165,7 @@ void main() { child: FlutterFormInputPlainText( label: const Text('Field2Label'), controller: testField2Controller, + validationMessage: 'Please fill in this field', ), ), ), @@ -260,6 +264,7 @@ void main() { child: FlutterFormInputPlainText( label: const Text('Field1Label'), controller: testField1Controller, + validationMessage: 'Field can not be empty', ), ), ),