From 094c1fd45210314d6ac6296f4798f7f3fe0ddb73 Mon Sep 17 00:00:00 2001 From: Golek Date: Mon, 5 Dec 2022 10:55:14 +0700 Subject: [PATCH] feat: reactive input field forms library (reactive form) --- .../lib/src/input_field.dart | 31 +++-- packages/features/landing_menu/pubspec.lock | 9 +- .../features/subscription_menu/pubspec.lock | 9 +- packages/form_fields/bin/form_fields.dart | 5 - packages/form_fields/l10n.yaml | 6 + packages/form_fields/lib/form_fields.dart | 4 +- .../form_fields/lib/src/error_messages.dart | 26 +++++ .../form_fields/lib/src/l10n/messages_en.arb | 13 +++ .../form_fields/lib/src/l10n/messages_id.arb | 13 +++ .../lib/src/reactive_input_field.dart | 69 +++++++++++ packages/form_fields/pubspec.lock | 109 +++++++++++++++++- packages/form_fields/pubspec.yaml | 13 ++- .../form_fields/test/form_fields_test.dart | 5 +- pubspec.lock | 9 +- pubspec.yaml | 2 + 15 files changed, 295 insertions(+), 28 deletions(-) delete mode 100644 packages/form_fields/bin/form_fields.dart create mode 100644 packages/form_fields/l10n.yaml create mode 100644 packages/form_fields/lib/src/error_messages.dart create mode 100644 packages/form_fields/lib/src/l10n/messages_en.arb create mode 100644 packages/form_fields/lib/src/l10n/messages_id.arb create mode 100644 packages/form_fields/lib/src/reactive_input_field.dart diff --git a/packages/component_library/lib/src/input_field.dart b/packages/component_library/lib/src/input_field.dart index 3f3d4d0..e0854b0 100644 --- a/packages/component_library/lib/src/input_field.dart +++ b/packages/component_library/lib/src/input_field.dart @@ -4,10 +4,8 @@ import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'error_message_builder.dart'; - class InputField extends StatefulWidget { - final String? label; + final String label; final String placeholder; final Widget? suffixIcon; final Widget? suffix; @@ -16,15 +14,16 @@ class InputField extends StatefulWidget { final String? errorMessage; final bool obscureText; final TextInputType textInputType; - final List? inputFormatters; final bool isDisabled; final bool isTouched; final bool isError; + final List? inputFormatters; + final void Function(String)? onChange; const InputField({ Key? key, required this.placeholder, - this.label, + required this.label, this.suffixIcon, this.suffix, this.prefix, @@ -36,6 +35,7 @@ class InputField extends StatefulWidget { required this.isDisabled, required this.isTouched, required this.isError, + this.onChange, }) : super(key: key); @override @@ -43,8 +43,15 @@ class InputField extends StatefulWidget { } class _InputFieldState extends State { + final _controller = TextEditingController(); bool _hasFocus = false; + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final theme = GolekTheme.of(context); @@ -53,9 +60,9 @@ class _InputFieldState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - if (widget.label != null && widget.label?.isEmpty == true) ...[ + if (widget.label.isEmpty == false) ...[ Text( - widget.label!, + widget.label, style: theme.textStyle.copyWith(fontSize: FontSize.style14), ), ], @@ -74,6 +81,14 @@ class _InputFieldState extends State { borderRadius: BorderRadius.circular(8.0), ), child: TextField( + controller: _controller, + onChanged: (value) { + _controller.text = value; + _controller.selection = TextSelection.fromPosition( + TextPosition(offset: _controller.text.length), + ); + widget.onChange?.call(value); + }, inputFormatters: widget.inputFormatters, keyboardType: widget.textInputType, obscureText: widget.obscureText, @@ -119,7 +134,7 @@ class _InputFieldState extends State { ), ), ErrorMessageBuilder( - inputLabel: widget.label ?? '', + inputLabel: widget.label, messageError: widget.errorMessage ?? '', ) ], diff --git a/packages/features/landing_menu/pubspec.lock b/packages/features/landing_menu/pubspec.lock index d776035..086e2a0 100644 --- a/packages/features/landing_menu/pubspec.lock +++ b/packages/features/landing_menu/pubspec.lock @@ -324,6 +324,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + reactive_forms: + dependency: transitive + description: + name: reactive_forms + url: "https://pub.dartlang.org" + source: hosted + version: "14.1.0" shelf: dependency: transitive description: @@ -485,4 +492,4 @@ packages: version: "3.1.1" sdks: dart: ">=2.18.2 <3.0.0" - flutter: ">=2.11.0-0.1.pre" + flutter: ">=3.0.0" diff --git a/packages/features/subscription_menu/pubspec.lock b/packages/features/subscription_menu/pubspec.lock index d776035..086e2a0 100644 --- a/packages/features/subscription_menu/pubspec.lock +++ b/packages/features/subscription_menu/pubspec.lock @@ -324,6 +324,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + reactive_forms: + dependency: transitive + description: + name: reactive_forms + url: "https://pub.dartlang.org" + source: hosted + version: "14.1.0" shelf: dependency: transitive description: @@ -485,4 +492,4 @@ packages: version: "3.1.1" sdks: dart: ">=2.18.2 <3.0.0" - flutter: ">=2.11.0-0.1.pre" + flutter: ">=3.0.0" diff --git a/packages/form_fields/bin/form_fields.dart b/packages/form_fields/bin/form_fields.dart deleted file mode 100644 index a4074d7..0000000 --- a/packages/form_fields/bin/form_fields.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:form_fields/form_fields.dart' as form_fields; - -void main(List arguments) { - print('Hello world: ${form_fields.calculate()}!'); -} diff --git a/packages/form_fields/l10n.yaml b/packages/form_fields/l10n.yaml new file mode 100644 index 0000000..46cce5a --- /dev/null +++ b/packages/form_fields/l10n.yaml @@ -0,0 +1,6 @@ +arb-dir: lib/src/l10n +template-arb-file: messages_en.arb +output-localization-file: form_fields_localizations.dart +output-class: FormFieldsLocalizations +synthetic-package: false +nullable-getter: false \ No newline at end of file diff --git a/packages/form_fields/lib/form_fields.dart b/packages/form_fields/lib/form_fields.dart index f64ad72..a4d1513 100644 --- a/packages/form_fields/lib/form_fields.dart +++ b/packages/form_fields/lib/form_fields.dart @@ -1,3 +1 @@ -int calculate() { - return 6 * 7; -} +export 'src/reactive_input_field.dart'; \ No newline at end of file diff --git a/packages/form_fields/lib/src/error_messages.dart b/packages/form_fields/lib/src/error_messages.dart new file mode 100644 index 0000000..d7d797c --- /dev/null +++ b/packages/form_fields/lib/src/error_messages.dart @@ -0,0 +1,26 @@ +import 'package:reactive_forms/reactive_forms.dart'; + +class ErrorMessages { + static const messages = { + 'required': '{_field_} harus diisi', + 'email': 'Format email harus benar', + }; + + static String getUiErrorMessage({ + required AbstractControl control, + required String label, + Map? widgetCustomMessages, + }) { + Map? mergedErrorMessages = { + ...?widgetCustomMessages, + ...messages, + }; + + final message = mergedErrorMessages.entries.firstWhere( + (element) => element.key == control.errors.entries.first.key, + orElse: () => const MapEntry('', ''), + ); + + return message.value.toString().replaceFirst('{_field_}', label); + } +} diff --git a/packages/form_fields/lib/src/l10n/messages_en.arb b/packages/form_fields/lib/src/l10n/messages_en.arb new file mode 100644 index 0000000..e2b441e --- /dev/null +++ b/packages/form_fields/lib/src/l10n/messages_en.arb @@ -0,0 +1,13 @@ +{ + "downvoteIconButtonTooltip": "Downvote", + "upvoteIconButtonTooltip": "Upvote", + "searchBarHintText": "journey", + "searchBarLabelText": "Search", + "shareIconButtonTooltip": "Share", + "favoriteIconButtonTooltip": "Favorite", + "exceptionIndicatorGenericTitle": "Something went wrong", + "exceptionIndicatorTryAgainButton": "Try Again", + "exceptionIndicatorGenericMessage": "There has been an error.\nPlease, check your internet connection and try again later.", + "genericErrorSnackbarMessage": "There has been an error. Please, check your internet connection.", + "authenticationRequiredErrorSnackbarMessage": "You need to sign in before performing this action." +} \ No newline at end of file diff --git a/packages/form_fields/lib/src/l10n/messages_id.arb b/packages/form_fields/lib/src/l10n/messages_id.arb new file mode 100644 index 0000000..e2b441e --- /dev/null +++ b/packages/form_fields/lib/src/l10n/messages_id.arb @@ -0,0 +1,13 @@ +{ + "downvoteIconButtonTooltip": "Downvote", + "upvoteIconButtonTooltip": "Upvote", + "searchBarHintText": "journey", + "searchBarLabelText": "Search", + "shareIconButtonTooltip": "Share", + "favoriteIconButtonTooltip": "Favorite", + "exceptionIndicatorGenericTitle": "Something went wrong", + "exceptionIndicatorTryAgainButton": "Try Again", + "exceptionIndicatorGenericMessage": "There has been an error.\nPlease, check your internet connection and try again later.", + "genericErrorSnackbarMessage": "There has been an error. Please, check your internet connection.", + "authenticationRequiredErrorSnackbarMessage": "You need to sign in before performing this action." +} \ No newline at end of file diff --git a/packages/form_fields/lib/src/reactive_input_field.dart b/packages/form_fields/lib/src/reactive_input_field.dart new file mode 100644 index 0000000..8044233 --- /dev/null +++ b/packages/form_fields/lib/src/reactive_input_field.dart @@ -0,0 +1,69 @@ +import 'package:component_library/component_library.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:form_fields/src/error_messages.dart'; +import 'package:reactive_forms/reactive_forms.dart'; + +class ReactiveInputField extends StatefulWidget { + const ReactiveInputField({ + Key? key, + required this.name, + required this.label, + required this.placeholder, + required this.obscureText, + required this.textInputType, + this.suffixIcon, + this.suffix, + this.prefix, + this.prefixIcon, + this.inputFormatters, + this.customErrorMessages, + }) : super(key: key); + + final String name; + final String label; + final String placeholder; + final Widget? suffixIcon; + final Widget? suffix; + final Widget? prefix; + final Widget? prefixIcon; + final bool obscureText; + final TextInputType textInputType; + final List? inputFormatters; + final Map? customErrorMessages; + + @override + State createState() => _ReactiveInputFieldState(); +} + +class _ReactiveInputFieldState extends State> { + @override + Widget build(BuildContext context) { + return ReactiveValueListenableBuilder( + formControlName: widget.name, + builder: (context, control, child) { + return InputField( + label: widget.label, + onChange: (value) { + control.value = value as T?; + }, + placeholder: widget.placeholder, + textInputType: widget.textInputType, + obscureText: widget.obscureText, + errorMessage: ErrorMessages.getUiErrorMessage( + control: control, + label: widget.label, + ), + inputFormatters: widget.inputFormatters, + prefix: widget.prefix, + prefixIcon: widget.prefixIcon, + suffix: widget.suffix, + suffixIcon: widget.suffixIcon, + isDisabled: control.disabled, + isTouched: control.touched, + isError: control.hasErrors, + ); + }, + ); + } +} diff --git a/packages/form_fields/pubspec.lock b/packages/form_fields/pubspec.lock index c362f1c..19e6722 100644 --- a/packages/form_fields/pubspec.lock +++ b/packages/form_fields/pubspec.lock @@ -29,6 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.10.0" + auto_size_text: + dependency: transitive + description: + name: auto_size_text + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -36,13 +43,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.17.0" + version: "1.16.0" + component_library: + dependency: "direct main" + description: + path: "../component_library" + relative: true + source: path + version: "0.0.0" convert: dependency: transitive description: @@ -71,6 +99,23 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_svg: + dependency: transitive + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" frontend_server_client: dependency: transitive description: @@ -99,6 +144,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" io: dependency: transitive description: @@ -134,6 +186,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" meta: dependency: transitive description: @@ -169,6 +228,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" pool: dependency: transitive description: @@ -183,6 +263,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + reactive_forms: + dependency: "direct main" + description: + name: reactive_forms + url: "https://pub.dartlang.org" + source: hosted + version: "14.1.0" shelf: dependency: transitive description: @@ -211,6 +298,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" source_map_stack_trace: dependency: transitive description: @@ -288,6 +380,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" vm_service: dependency: transitive description: @@ -316,6 +415,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" yaml: dependency: transitive description: @@ -325,3 +431,4 @@ packages: version: "3.1.1" sdks: dart: ">=2.18.2 <3.0.0" + flutter: ">=3.0.0" diff --git a/packages/form_fields/pubspec.yaml b/packages/form_fields/pubspec.yaml index 8bd6432..9033458 100644 --- a/packages/form_fields/pubspec.yaml +++ b/packages/form_fields/pubspec.yaml @@ -1,14 +1,19 @@ name: form_fields description: A sample command-line application. version: 1.0.0 -# homepage: https://www.example.com +publish_to: none environment: sdk: '>=2.18.2 <3.0.0' -# dependencies: -# path: ^1.8.0 - +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + reactive_forms: ^14.1.0 + component_library: + path: ../component_library dev_dependencies: lints: ^2.0.0 test: ^1.16.0 diff --git a/packages/form_fields/test/form_fields_test.dart b/packages/form_fields/test/form_fields_test.dart index 30976ad..7bea434 100644 --- a/packages/form_fields/test/form_fields_test.dart +++ b/packages/form_fields/test/form_fields_test.dart @@ -1,8 +1,5 @@ import 'package:form_fields/form_fields.dart'; -import 'package:test/test.dart'; void main() { - test('calculate', () { - expect(calculate(), 42); - }); + } diff --git a/pubspec.lock b/pubspec.lock index 4582a63..15fd050 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -358,7 +358,7 @@ packages: source: sdk version: "0.0.0" form_fields: - dependency: transitive + dependency: "direct main" description: path: "packages/form_fields" relative: true @@ -560,6 +560,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.1" + reactive_forms: + dependency: transitive + description: + name: reactive_forms + url: "https://pub.dartlang.org" + source: hosted + version: "14.1.0" shelf: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c90e68b..6777bff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: path: packages/domain_models component_library: path: packages/component_library + form_fields: + path: packages/form_fields subscription_menu: path: packages/features/subscription_menu landing_menu: