diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f8171cf4..108f0b3ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# 10.0.0 + +## BREAKING CHANGES + +* Set minimal Flutter version to 3.29.0 +* Set minimal Dart version to 3.7.0 +* refactor: #1456 remove deprecated code by @deandreamatias in https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/pull/1465 + * Solve issue #1456 + * Remove `invalidateField` and `invalidateFirstField` methods from FormBuilderState. Use `fields[name]?.invalidate(errorText)` and `fields.first.invalidate(errorText)` instead. + * [FormBuilderTextField] Deprecate canRequestFocus property. Use `FocusNode.canRequestFocus.` instead. + * Assert on FormBuilderField.decoration.enabled property. Use FormBuilderField.enabled instead. + * Easy way! Only need execute `dart fix --apply` on your project to fix the following changes: + * Rename FormBuilderChoiceChip to FormBuilderChoiceChips. + * Rename FormBuilderFilterChip to FormBuilderFilterChips. + * [FormBuilderFilterChip] Remove maxChips property. + * [FormBuilderDateTimePicker] Remove resetIcon property. + * [FormBuilder] Remove onPopInvoked property. + +## Features + +* feat: #1455 improve input decoration enabled property by @deandreamatias in https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/pull/1464 + * Solve issue #1455 +* feat: #1458 improve autovalidate modes by @deandreamatias in https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/pull/1460 + * Solves issues #1364 and #1457 +* feat: #1297 Improve focus behavior by @deandreamatias in https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/pull/1453 + * Solves issues #1296, #1290, #1301, #1304 and #1292 + # 10.0.0-dev.3 ## BREAKING CHANGES @@ -7,7 +34,7 @@ * Remove `invalidateField` and `invalidateFirstField` methods from FormBuilderState. Use `fields[name]?.invalidate(errorText)` and `fields.first.invalidate(errorText)` instead. * [FormBuilderTextField] Deprecate canRequestFocus property. Use `FocusNode.canRequestFocus.` instead. * Assert on FormBuilderField.decoration.enabled property. Use FormBuilderField.enabled instead. - * Easy way! Only need execute `dart fix --apply` on your project. + * Easy way! Only need execute `dart fix --apply` on your project to fix the following changes: * Rename FormBuilderChoiceChip to FormBuilderChoiceChips. * Rename FormBuilderFilterChip to FormBuilderFilterChips. * [FormBuilderFilterChip] Remove maxChips property. diff --git a/README.md b/README.md index 6c4d66662..05b2c9afa 100644 --- a/README.md +++ b/README.md @@ -414,7 +414,7 @@ class _ClearFormBuilderTextFieldState - Remove `invalidateField` and `invalidateFirstField` methods from FormBuilderState. Use `fields[name]?.invalidate(errorText)` and `fields.first.invalidate(errorText)` instead - [FormBuilderTextField] Deprecate canRequestFocus property. Use `FocusNode.canRequestFocus.` instead - Assert on FormBuilderField.decoration.enabled property. Use FormBuilderField.enabled instead -- Easy way! Only need execute `dart fix --apply` on your project. +- Easy way! Only need execute `dart fix --apply` on your project to fix the following changes: - Rename FormBuilderChoiceChip to FormBuilderChoiceChips. - Rename FormBuilderFilterChip to FormBuilderFilterChips. - [FormBuilderFilterChip] Remove maxChips property. diff --git a/example/lib/minimal_code_example.dart b/example/lib/minimal_code_example.dart index bbc60d825..99d06a5c2 100644 --- a/example/lib/minimal_code_example.dart +++ b/example/lib/minimal_code_example.dart @@ -47,7 +47,6 @@ class _ExamplePageState extends State<_ExamplePage> { FormBuilderFilterChips( decoration: const InputDecoration( labelText: 'The language of my people', - enabled: false, ), name: 'languages_filter', selectedColor: Colors.red, diff --git a/example/pubspec.lock b/example/pubspec.lock index 9e49a60c7..6df03e79b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -99,18 +99,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -123,18 +123,18 @@ packages: dependency: transitive description: name: lints - sha256: "4a16b3f03741e1252fda5de3ce712666d010ba2122f8e912c94f9f7b90e1a4c3" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.1.1" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -147,18 +147,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -168,50 +168,50 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" vector_math: dependency: transitive description: @@ -224,10 +224,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.27.0" + dart: ">=3.7.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/lib/src/fields/form_builder_checkbox.dart b/lib/src/fields/form_builder_checkbox.dart index c3939fb70..8d7f5ba01 100644 --- a/lib/src/fields/form_builder_checkbox.dart +++ b/lib/src/fields/form_builder_checkbox.dart @@ -120,39 +120,40 @@ class FormBuilderCheckbox extends FormBuilderFieldDecoration { this.shape, this.side, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderCheckboxState; - - return InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: CheckboxListTile( - dense: true, - isThreeLine: false, - focusNode: state.effectiveFocusNode, - title: title, - subtitle: subtitle, - value: tristate ? state.value : (state.value ?? false), - onChanged: state.enabled - ? (value) { - state.didChange(value); - } - : null, - checkColor: checkColor, - activeColor: activeColor, - secondary: secondary, - controlAffinity: controlAffinity, - autofocus: autofocus, - tristate: tristate, - contentPadding: contentPadding, - visualDensity: visualDensity, - selected: selected, - checkboxShape: shape, - side: side, - ), - ); - }, - ); + builder: (FormFieldState field) { + final state = field as _FormBuilderCheckboxState; + + return InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: CheckboxListTile( + dense: true, + isThreeLine: false, + focusNode: state.effectiveFocusNode, + title: title, + subtitle: subtitle, + value: tristate ? state.value : (state.value ?? false), + onChanged: + state.enabled + ? (value) { + state.didChange(value); + } + : null, + checkColor: checkColor, + activeColor: activeColor, + secondary: secondary, + controlAffinity: controlAffinity, + autofocus: autofocus, + tristate: tristate, + contentPadding: contentPadding, + visualDensity: visualDensity, + selected: selected, + checkboxShape: shape, + side: side, + ), + ); + }, + ); @override FormBuilderFieldDecorationState createState() => diff --git a/lib/src/fields/form_builder_checkbox_group.dart b/lib/src/fields/form_builder_checkbox_group.dart index 95ac918a2..d40f5350f 100644 --- a/lib/src/fields/form_builder_checkbox_group.dart +++ b/lib/src/fields/form_builder_checkbox_group.dart @@ -66,55 +66,57 @@ class FormBuilderCheckboxGroup extends FormBuilderFieldDecoration> { this.orientation = OptionsOrientation.wrap, this.itemDecoration, }) : super( - builder: (FormFieldState?> field) { - final state = field as _FormBuilderCheckboxGroupState; + builder: (FormFieldState?> field) { + final state = field as _FormBuilderCheckboxGroupState; - return Focus( - focusNode: state.effectiveFocusNode, - skipTraversal: true, - canRequestFocus: state.enabled, - debugLabel: 'FormBuilderCheckboxGroup-$name', - child: InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: GroupedCheckbox( - orientation: orientation, - value: state.value, - options: options, - onChanged: (val) { - field.didChange(val); - }, - disabled: state.enabled - ? disabled - : options.map((e) => e.value).toList(), - activeColor: activeColor, - visualDensity: visualDensity, - focusColor: focusColor, - checkColor: checkColor, - materialTapTargetSize: materialTapTargetSize, - hoverColor: hoverColor, - tristate: tristate, - wrapAlignment: wrapAlignment, - wrapCrossAxisAlignment: wrapCrossAxisAlignment, - wrapDirection: wrapDirection, - wrapRunAlignment: wrapRunAlignment, - wrapRunSpacing: wrapRunSpacing, - wrapSpacing: wrapSpacing, - wrapTextDirection: wrapTextDirection, - wrapVerticalDirection: wrapVerticalDirection, - separator: separator, - controlAffinity: controlAffinity, - itemDecoration: itemDecoration, - ), - ), - ); - }, - ); + return Focus( + focusNode: state.effectiveFocusNode, + skipTraversal: true, + canRequestFocus: state.enabled, + debugLabel: 'FormBuilderCheckboxGroup-$name', + child: InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: GroupedCheckbox( + orientation: orientation, + value: state.value, + options: options, + onChanged: (val) { + field.didChange(val); + }, + disabled: + state.enabled + ? disabled + : options.map((e) => e.value).toList(), + activeColor: activeColor, + visualDensity: visualDensity, + focusColor: focusColor, + checkColor: checkColor, + materialTapTargetSize: materialTapTargetSize, + hoverColor: hoverColor, + tristate: tristate, + wrapAlignment: wrapAlignment, + wrapCrossAxisAlignment: wrapCrossAxisAlignment, + wrapDirection: wrapDirection, + wrapRunAlignment: wrapRunAlignment, + wrapRunSpacing: wrapRunSpacing, + wrapSpacing: wrapSpacing, + wrapTextDirection: wrapTextDirection, + wrapVerticalDirection: wrapVerticalDirection, + separator: separator, + controlAffinity: controlAffinity, + itemDecoration: itemDecoration, + ), + ), + ); + }, + ); @override FormBuilderFieldDecorationState, List> - createState() => _FormBuilderCheckboxGroupState(); + createState() => _FormBuilderCheckboxGroupState(); } -class _FormBuilderCheckboxGroupState extends FormBuilderFieldDecorationState< - FormBuilderCheckboxGroup, List> {} +class _FormBuilderCheckboxGroupState + extends + FormBuilderFieldDecorationState, List> {} diff --git a/lib/src/fields/form_builder_choice_chips.dart b/lib/src/fields/form_builder_choice_chips.dart index 890de3880..5322fcf83 100644 --- a/lib/src/fields/form_builder_choice_chips.dart +++ b/lib/src/fields/form_builder_choice_chips.dart @@ -392,69 +392,72 @@ class FormBuilderChoiceChips extends FormBuilderFieldDecoration { this.color, this.iconTheme, this.tooltip, - }) : super(builder: (FormFieldState field) { - final state = field as _FormBuilderChoiceChipState; - - return Focus( - focusNode: state.effectiveFocusNode, - skipTraversal: true, - canRequestFocus: state.enabled, - debugLabel: 'FormBuilderChoiceChip-$name', - child: InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: Wrap( - direction: direction, - alignment: alignment, - crossAxisAlignment: crossAxisAlignment, - runAlignment: runAlignment, - runSpacing: runSpacing, - spacing: spacing, - textDirection: textDirection, - verticalDirection: verticalDirection, - children: [ - for (FormBuilderChipOption option in options) - ChoiceChip( - label: option, - side: side, - shape: shape, - selected: field.value == option.value, - onSelected: state.enabled - ? (selected) { - final choice = selected ? option.value : null; - state.didChange(choice); - } - : null, - avatar: option.avatar, - selectedColor: selectedColor, - disabledColor: disabledColor, - backgroundColor: backgroundColor, - shadowColor: shadowColor, - selectedShadowColor: selectedShadowColor, - elevation: elevation, - pressElevation: pressElevation, - materialTapTargetSize: materialTapTargetSize, - labelStyle: labelStyle, - labelPadding: labelPadding, - padding: padding, - visualDensity: visualDensity, - avatarBorder: avatarBorder, - showCheckmark: showCheckmark, - surfaceTintColor: surfaceTintColor, - clipBehavior: clipBehavior, - checkmarkColor: checkmarkColor, - autofocus: autofocus, - avatarBoxConstraints: avatarBoxConstraints, - chipAnimationStyle: chipAnimationStyle, - color: color, - iconTheme: iconTheme, - tooltip: tooltip, - ), - ], - ), - ), - ); - }); + }) : super( + builder: (FormFieldState field) { + final state = field as _FormBuilderChoiceChipState; + + return Focus( + focusNode: state.effectiveFocusNode, + skipTraversal: true, + canRequestFocus: state.enabled, + debugLabel: 'FormBuilderChoiceChip-$name', + child: InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: Wrap( + direction: direction, + alignment: alignment, + crossAxisAlignment: crossAxisAlignment, + runAlignment: runAlignment, + runSpacing: runSpacing, + spacing: spacing, + textDirection: textDirection, + verticalDirection: verticalDirection, + children: [ + for (FormBuilderChipOption option in options) + ChoiceChip( + label: option, + side: side, + shape: shape, + selected: field.value == option.value, + onSelected: + state.enabled + ? (selected) { + final choice = selected ? option.value : null; + state.didChange(choice); + } + : null, + avatar: option.avatar, + selectedColor: selectedColor, + disabledColor: disabledColor, + backgroundColor: backgroundColor, + shadowColor: shadowColor, + selectedShadowColor: selectedShadowColor, + elevation: elevation, + pressElevation: pressElevation, + materialTapTargetSize: materialTapTargetSize, + labelStyle: labelStyle, + labelPadding: labelPadding, + padding: padding, + visualDensity: visualDensity, + avatarBorder: avatarBorder, + showCheckmark: showCheckmark, + surfaceTintColor: surfaceTintColor, + clipBehavior: clipBehavior, + checkmarkColor: checkmarkColor, + autofocus: autofocus, + avatarBoxConstraints: avatarBoxConstraints, + chipAnimationStyle: chipAnimationStyle, + color: color, + iconTheme: iconTheme, + tooltip: tooltip, + ), + ], + ), + ), + ); + }, + ); @override FormBuilderFieldDecorationState, T> createState() => diff --git a/lib/src/fields/form_builder_date_range_picker.dart b/lib/src/fields/form_builder_date_range_picker.dart index 5217da312..ff4eacc77 100644 --- a/lib/src/fields/form_builder_date_range_picker.dart +++ b/lib/src/fields/form_builder_date_range_picker.dart @@ -48,7 +48,7 @@ class FormBuilderDateRangePicker final DateTime? currentDate; // widget.currentDate, final String? errorFormatText; // widget.erroerrorFormatText, final Widget Function(BuildContext, Widget?)? - pickerBuilder; // widget.builder, + pickerBuilder; // widget.builder, final String? errorInvalidRangeText; // widget.errorInvalidRangeText, final String? errorInvalidText; // widget.errorInvalidText, final String? fieldEndHintText; // widget.fieldEndHintText, @@ -131,54 +131,54 @@ class FormBuilderDateRangePicker this.allowClear = false, this.clearIcon, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderDateRangePickerState; + builder: (FormFieldState field) { + final state = field as _FormBuilderDateRangePickerState; - return FocusTraversalGroup( - policy: ReadingOrderTraversalPolicy(), - child: TextField( - onTap: () => state.showPicker(), - enabled: state.enabled, - style: style, - focusNode: state.effectiveFocusNode, - decoration: state.decoration, - // initialValue: "${_initialValue ?? ''}", - maxLines: maxLines, - keyboardType: keyboardType, - obscureText: obscureText, - onEditingComplete: onEditingComplete, - controller: state._effectiveController, - autocorrect: autocorrect, - autofocus: autofocus, - buildCounter: buildCounter, - mouseCursor: mouseCursor, - cursorColor: cursorColor, - cursorRadius: cursorRadius, - cursorWidth: cursorWidth, - enableInteractiveSelection: enableInteractiveSelection, - maxLength: maxLength, - inputFormatters: inputFormatters, - keyboardAppearance: keyboardAppearance, - maxLengthEnforcement: maxLengthEnforcement, - scrollPadding: scrollPadding, - textAlign: textAlign, - textCapitalization: textCapitalization, - textDirection: textDirection, - textInputAction: textInputAction, - textAlignVertical: textAlignVertical, - strutStyle: strutStyle, - readOnly: true, - expands: expands, - minLines: minLines, - showCursor: showCursor, - ), - ); - }, - ); + return FocusTraversalGroup( + policy: ReadingOrderTraversalPolicy(), + child: TextField( + onTap: () => state.showPicker(), + enabled: state.enabled, + style: style, + focusNode: state.effectiveFocusNode, + decoration: state.decoration, + // initialValue: "${_initialValue ?? ''}", + maxLines: maxLines, + keyboardType: keyboardType, + obscureText: obscureText, + onEditingComplete: onEditingComplete, + controller: state._effectiveController, + autocorrect: autocorrect, + autofocus: autofocus, + buildCounter: buildCounter, + mouseCursor: mouseCursor, + cursorColor: cursorColor, + cursorRadius: cursorRadius, + cursorWidth: cursorWidth, + enableInteractiveSelection: enableInteractiveSelection, + maxLength: maxLength, + inputFormatters: inputFormatters, + keyboardAppearance: keyboardAppearance, + maxLengthEnforcement: maxLengthEnforcement, + scrollPadding: scrollPadding, + textAlign: textAlign, + textCapitalization: textCapitalization, + textDirection: textDirection, + textInputAction: textInputAction, + textAlignVertical: textAlignVertical, + strutStyle: strutStyle, + readOnly: true, + expands: expands, + minLines: minLines, + showCursor: showCursor, + ), + ); + }, + ); @override FormBuilderFieldDecorationState - createState() => _FormBuilderDateRangePickerState(); + createState() => _FormBuilderDateRangePickerState(); static String tryFormat(DateTime date, intl.DateFormat format) { try { @@ -190,8 +190,12 @@ class FormBuilderDateRangePicker } } -class _FormBuilderDateRangePickerState extends FormBuilderFieldDecorationState< - FormBuilderDateRangePicker, DateTimeRange> { +class _FormBuilderDateRangePickerState + extends + FormBuilderFieldDecorationState< + FormBuilderDateRangePicker, + DateTimeRange + > { late TextEditingController _effectiveController; @override @@ -262,7 +266,9 @@ class _FormBuilderDateRangePickerState extends FormBuilderFieldDecorationState< } String format(DateTime date) => FormBuilderDateRangePicker.tryFormat( - date, widget.format ?? intl.DateFormat.yMd()); + date, + widget.format ?? intl.DateFormat.yMd(), + ); void _setTextFieldString() { setState(() => _effectiveController.text = _valueToText()); @@ -281,8 +287,9 @@ class _FormBuilderDateRangePickerState extends FormBuilderFieldDecorationState< } @override - InputDecoration get decoration => widget.allowClear - ? super.decoration.copyWith( + InputDecoration get decoration => + widget.allowClear + ? super.decoration.copyWith( suffix: IconButton( padding: EdgeInsets.zero, constraints: const BoxConstraints(maxWidth: 24, maxHeight: 24), @@ -293,5 +300,5 @@ class _FormBuilderDateRangePickerState extends FormBuilderFieldDecorationState< icon: widget.clearIcon ?? const Icon(Icons.clear), ), ) - : super.decoration; + : super.decoration; } diff --git a/lib/src/fields/form_builder_date_time_picker.dart b/lib/src/fields/form_builder_date_time_picker.dart index eb21a3e1d..cc378b2a2 100644 --- a/lib/src/fields/form_builder_date_time_picker.dart +++ b/lib/src/fields/form_builder_date_time_picker.dart @@ -193,57 +193,58 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { this.onEntryModeChanged, this.barrierDismissible = true, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderDateTimePickerState; - - return FocusTraversalGroup( - policy: ReadingOrderTraversalPolicy(), - child: TextField( - onTap: () => state.showPicker(), - textDirection: textDirection, - textAlign: textAlign, - textAlignVertical: textAlignVertical, - maxLength: maxLength, - autofocus: autofocus, - decoration: state.decoration, - readOnly: true, - enabled: state.enabled, - autocorrect: autocorrect, - controller: state._textFieldController, - focusNode: state.effectiveFocusNode, - inputFormatters: inputFormatters, - keyboardType: keyboardType, - maxLines: maxLines, - obscureText: obscureText, - showCursor: showCursor, - minLines: minLines, - expands: expands, - style: style, - onEditingComplete: onEditingComplete, - buildCounter: buildCounter, - mouseCursor: mouseCursor, - cursorColor: cursorColor, - cursorRadius: cursorRadius, - cursorWidth: cursorWidth, - enableInteractiveSelection: enableInteractiveSelection, - keyboardAppearance: keyboardAppearance, - scrollPadding: scrollPadding, - strutStyle: strutStyle, - textCapitalization: textCapitalization, - textInputAction: textInputAction, - maxLengthEnforcement: maxLengthEnforcement, - ), - ); - }, - ); + builder: (FormFieldState field) { + final state = field as _FormBuilderDateTimePickerState; + + return FocusTraversalGroup( + policy: ReadingOrderTraversalPolicy(), + child: TextField( + onTap: () => state.showPicker(), + textDirection: textDirection, + textAlign: textAlign, + textAlignVertical: textAlignVertical, + maxLength: maxLength, + autofocus: autofocus, + decoration: state.decoration, + readOnly: true, + enabled: state.enabled, + autocorrect: autocorrect, + controller: state._textFieldController, + focusNode: state.effectiveFocusNode, + inputFormatters: inputFormatters, + keyboardType: keyboardType, + maxLines: maxLines, + obscureText: obscureText, + showCursor: showCursor, + minLines: minLines, + expands: expands, + style: style, + onEditingComplete: onEditingComplete, + buildCounter: buildCounter, + mouseCursor: mouseCursor, + cursorColor: cursorColor, + cursorRadius: cursorRadius, + cursorWidth: cursorWidth, + enableInteractiveSelection: enableInteractiveSelection, + keyboardAppearance: keyboardAppearance, + scrollPadding: scrollPadding, + strutStyle: strutStyle, + textCapitalization: textCapitalization, + textInputAction: textInputAction, + maxLengthEnforcement: maxLengthEnforcement, + ), + ); + }, + ); @override FormBuilderFieldDecorationState - createState() => _FormBuilderDateTimePickerState(); + createState() => _FormBuilderDateTimePickerState(); } -class _FormBuilderDateTimePickerState extends FormBuilderFieldDecorationState< - FormBuilderDateTimePicker, DateTime> { +class _FormBuilderDateTimePickerState + extends + FormBuilderFieldDecorationState { late TextEditingController _textFieldController; late DateFormat _dateFormat; @@ -283,7 +284,7 @@ class _FormBuilderDateTimePickerState extends FormBuilderFieldDecorationState< return switch (widget.inputType) { InputType.time => DateFormat.Hm(languageCode), InputType.date => DateFormat.yMd(languageCode), - InputType.both => DateFormat.yMd(languageCode).add_Hms() + InputType.both => DateFormat.yMd(languageCode).add_Hms(), }; } @@ -358,18 +359,20 @@ class _FormBuilderDateTimePickerState extends FormBuilderFieldDecorationState< return Localizations.override( context: context, locale: widget.locale, - child: transitionBuilder == null - ? child - : transitionBuilder(context, child), + child: + transitionBuilder == null + ? child + : transitionBuilder(context, child), ); }; } return await showTimePicker( context: context, - initialTime: currentValue != null - ? TimeOfDay.fromDateTime(currentValue) - : widget.initialTime, + initialTime: + currentValue != null + ? TimeOfDay.fromDateTime(currentValue) + : widget.initialTime, builder: builder, useRootNavigator: widget.useRootNavigator, routeSettings: widget.routeSettings, @@ -386,7 +389,12 @@ class _FormBuilderDateTimePickerState extends FormBuilderFieldDecorationState< /// Sets the hour and minute of a [DateTime] from a [TimeOfDay]. DateTime combine(DateTime date, TimeOfDay? time) => DateTime( - date.year, date.month, date.day, time?.hour ?? 0, time?.minute ?? 0); + date.year, + date.month, + date.day, + time?.hour ?? 0, + time?.minute ?? 0, + ); DateTime? convert(TimeOfDay? time) => time == null ? null : DateTime(1, 1, 1, time.hour, time.minute); diff --git a/lib/src/fields/form_builder_dropdown.dart b/lib/src/fields/form_builder_dropdown.dart index a70151dac..87f9dfacb 100644 --- a/lib/src/fields/form_builder_dropdown.dart +++ b/lib/src/fields/form_builder_dropdown.dart @@ -289,53 +289,57 @@ class FormBuilderDropdown extends FormBuilderFieldDecoration { this.padding, this.menuWidth, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderDropdownState; + builder: (FormFieldState field) { + final state = field as _FormBuilderDropdownState; - final hasValue = items.map((e) => e.value).contains(field.value); - return InputDecorator( - decoration: state.decoration, - child: DropdownButton( - menuWidth: menuWidth, - padding: padding, - underline: underline, - isExpanded: isExpanded, - items: items, - value: hasValue ? field.value : null, - style: style, - isDense: isDense, - disabledHint: hasValue - ? items - .firstWhere( - (dropDownItem) => dropDownItem.value == field.value) - .child - : disabledHint, - elevation: elevation, - iconSize: iconSize, - icon: icon, - iconDisabledColor: iconDisabledColor, - iconEnabledColor: iconEnabledColor, - onChanged: state.enabled - ? (T? value) { - field.didChange(value); - } - : null, - onTap: onTap, - focusNode: state.effectiveFocusNode, - autofocus: autofocus, - dropdownColor: dropdownColor, - focusColor: focusColor, - itemHeight: itemHeight, - selectedItemBuilder: selectedItemBuilder, - menuMaxHeight: menuMaxHeight, - borderRadius: borderRadius, - enableFeedback: enableFeedback, - alignment: alignment, - hint: hint, - ), - ); - }, - ); + final hasValue = items.map((e) => e.value).contains(field.value); + return InputDecorator( + decoration: state.decoration, + child: DropdownButton( + menuWidth: menuWidth, + padding: padding, + underline: underline, + isExpanded: isExpanded, + items: items, + value: hasValue ? field.value : null, + style: style, + isDense: isDense, + disabledHint: + hasValue + ? items + .firstWhere( + (dropDownItem) => + dropDownItem.value == field.value, + ) + .child + : disabledHint, + elevation: elevation, + iconSize: iconSize, + icon: icon, + iconDisabledColor: iconDisabledColor, + iconEnabledColor: iconEnabledColor, + onChanged: + state.enabled + ? (T? value) { + field.didChange(value); + } + : null, + onTap: onTap, + focusNode: state.effectiveFocusNode, + autofocus: autofocus, + dropdownColor: dropdownColor, + focusColor: focusColor, + itemHeight: itemHeight, + selectedItemBuilder: selectedItemBuilder, + menuMaxHeight: menuMaxHeight, + borderRadius: borderRadius, + enableFeedback: enableFeedback, + alignment: alignment, + hint: hint, + ), + ); + }, + ); @override FormBuilderFieldDecorationState, T> createState() => diff --git a/lib/src/fields/form_builder_filter_chips.dart b/lib/src/fields/form_builder_filter_chips.dart index d8c4b7f52..b8c549a45 100644 --- a/lib/src/fields/form_builder_filter_chips.dart +++ b/lib/src/fields/form_builder_filter_chips.dart @@ -76,76 +76,78 @@ class FormBuilderFilterChips extends FormBuilderFieldDecoration> { super.onChanged, super.valueTransformer, super.onReset, - }) : assert((maxChips == null) || ((initialValue ?? []).length <= maxChips)), - super( - builder: (FormFieldState?> field) { - final state = field as _FormBuilderFilterChipState; - final fieldValue = field.value ?? []; - return Focus( - focusNode: state.effectiveFocusNode, - skipTraversal: true, - canRequestFocus: state.enabled, - debugLabel: 'FormBuilderFilterChip-$name', - child: InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: Wrap( - direction: direction, - alignment: alignment, - crossAxisAlignment: crossAxisAlignment, - runAlignment: runAlignment, - runSpacing: runSpacing, - spacing: spacing, - textDirection: textDirection, - verticalDirection: verticalDirection, - children: [ - for (FormBuilderChipOption option in options) - FilterChip( - label: option, - selected: fieldValue.contains(option.value), - avatar: option.avatar, - onSelected: state.enabled && - (null == maxChips || - fieldValue.length < maxChips || - fieldValue.contains(option.value)) - ? (selected) { - final currentValue = [...fieldValue]; - selected - ? currentValue.add(option.value) - : currentValue.remove(option.value); + }) : assert((maxChips == null) || ((initialValue ?? []).length <= maxChips)), + super( + builder: (FormFieldState?> field) { + final state = field as _FormBuilderFilterChipState; + final fieldValue = field.value ?? []; + return Focus( + focusNode: state.effectiveFocusNode, + skipTraversal: true, + canRequestFocus: state.enabled, + debugLabel: 'FormBuilderFilterChip-$name', + child: InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: Wrap( + direction: direction, + alignment: alignment, + crossAxisAlignment: crossAxisAlignment, + runAlignment: runAlignment, + runSpacing: runSpacing, + spacing: spacing, + textDirection: textDirection, + verticalDirection: verticalDirection, + children: [ + for (FormBuilderChipOption option in options) + FilterChip( + label: option, + selected: fieldValue.contains(option.value), + avatar: option.avatar, + onSelected: + state.enabled && + (null == maxChips || + fieldValue.length < maxChips || + fieldValue.contains(option.value)) + ? (selected) { + final currentValue = [...fieldValue]; + selected + ? currentValue.add(option.value) + : currentValue.remove(option.value); - field.didChange(currentValue); - } - : null, - selectedColor: selectedColor, - disabledColor: disabledColor, - backgroundColor: backgroundColor, - shadowColor: shadowColor, - selectedShadowColor: selectedShadowColor, - elevation: elevation, - pressElevation: pressElevation, - materialTapTargetSize: materialTapTargetSize, - padding: padding, - side: side, - shape: shape, - checkmarkColor: checkmarkColor, - clipBehavior: clipBehavior, - labelStyle: labelStyle, - showCheckmark: showCheckmark, - labelPadding: labelPadding, - avatarBorder: avatarBorder, - ), - ], - ), - ), - ); - }, - ); + field.didChange(currentValue); + } + : null, + selectedColor: selectedColor, + disabledColor: disabledColor, + backgroundColor: backgroundColor, + shadowColor: shadowColor, + selectedShadowColor: selectedShadowColor, + elevation: elevation, + pressElevation: pressElevation, + materialTapTargetSize: materialTapTargetSize, + padding: padding, + side: side, + shape: shape, + checkmarkColor: checkmarkColor, + clipBehavior: clipBehavior, + labelStyle: labelStyle, + showCheckmark: showCheckmark, + labelPadding: labelPadding, + avatarBorder: avatarBorder, + ), + ], + ), + ), + ); + }, + ); @override FormBuilderFieldDecorationState, List> - createState() => _FormBuilderFilterChipState(); + createState() => _FormBuilderFilterChipState(); } -class _FormBuilderFilterChipState extends FormBuilderFieldDecorationState< - FormBuilderFilterChips, List> {} +class _FormBuilderFilterChipState + extends + FormBuilderFieldDecorationState, List> {} diff --git a/lib/src/fields/form_builder_radio_group.dart b/lib/src/fields/form_builder_radio_group.dart index 94119d872..c470e097e 100644 --- a/lib/src/fields/form_builder_radio_group.dart +++ b/lib/src/fields/form_builder_radio_group.dart @@ -60,47 +60,48 @@ class FormBuilderRadioGroup extends FormBuilderFieldDecoration { super.restorationId, this.itemDecoration, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderRadioGroupState; + builder: (FormFieldState field) { + final state = field as _FormBuilderRadioGroupState; - return Focus( - focusNode: state.effectiveFocusNode, - skipTraversal: true, - canRequestFocus: state.enabled, - debugLabel: 'FormBuilderRadioGroup-$name', - child: InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: GroupedRadio( - activeColor: activeColor, - controlAffinity: controlAffinity, - disabled: state.enabled - ? disabled - : options.map((option) => option.value).toList(), - focusColor: focusColor, - hoverColor: hoverColor, - materialTapTargetSize: materialTapTargetSize, - onChanged: (value) { - state.didChange(value); - }, - options: options, - orientation: orientation, - separator: separator, - value: state.value, - wrapAlignment: wrapAlignment, - wrapCrossAxisAlignment: wrapCrossAxisAlignment, - wrapDirection: wrapDirection, - wrapRunAlignment: wrapRunAlignment, - wrapRunSpacing: wrapRunSpacing, - wrapSpacing: wrapSpacing, - wrapTextDirection: wrapTextDirection, - wrapVerticalDirection: wrapVerticalDirection, - itemDecoration: itemDecoration, - ), - ), - ); - }, - ); + return Focus( + focusNode: state.effectiveFocusNode, + skipTraversal: true, + canRequestFocus: state.enabled, + debugLabel: 'FormBuilderRadioGroup-$name', + child: InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: GroupedRadio( + activeColor: activeColor, + controlAffinity: controlAffinity, + disabled: + state.enabled + ? disabled + : options.map((option) => option.value).toList(), + focusColor: focusColor, + hoverColor: hoverColor, + materialTapTargetSize: materialTapTargetSize, + onChanged: (value) { + state.didChange(value); + }, + options: options, + orientation: orientation, + separator: separator, + value: state.value, + wrapAlignment: wrapAlignment, + wrapCrossAxisAlignment: wrapCrossAxisAlignment, + wrapDirection: wrapDirection, + wrapRunAlignment: wrapRunAlignment, + wrapRunSpacing: wrapRunSpacing, + wrapSpacing: wrapSpacing, + wrapTextDirection: wrapTextDirection, + wrapVerticalDirection: wrapVerticalDirection, + itemDecoration: itemDecoration, + ), + ), + ); + }, + ); @override FormBuilderFieldDecorationState, T> createState() => diff --git a/lib/src/fields/form_builder_range_slider.dart b/lib/src/fields/form_builder_range_slider.dart index de2293b44..0bf374dfc 100644 --- a/lib/src/fields/form_builder_range_slider.dart +++ b/lib/src/fields/form_builder_range_slider.dart @@ -188,87 +188,95 @@ class FormBuilderRangeSlider extends FormBuilderFieldDecoration { this.valueWidget, this.maxValueWidget, this.numberFormat, - }) : super(builder: (FormFieldState field) { - final state = field as _FormBuilderRangeSliderState; - final effectiveNumberFormat = numberFormat ?? NumberFormat.compact(); - if (field.value == null || - field.value!.start < min || - field.value!.start > max || - field.value!.end < min || - field.value!.end > max) { - if (initialValue == null) { - field.setValue(RangeValues(min, min)); - } else { - field.setValue( - RangeValues(initialValue.start, initialValue.end), - ); - } - } - // TODO: Solve focus issue when Flutter team solve this issue - // https://github.com/flutter/flutter/issues/53958 - return Focus( - focusNode: state.effectiveFocusNode, - skipTraversal: true, - canRequestFocus: state.enabled, - debugLabel: 'FormBuilderRangeSlider-$name', - child: InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: Container( - padding: const EdgeInsets.only(top: 10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RangeSlider( - values: field.value!, - min: min, - max: max, - divisions: divisions, - activeColor: activeColor, - inactiveColor: inactiveColor, - onChangeEnd: onChangeEnd, - onChangeStart: onChangeStart, - labels: labels, - semanticFormatterCallback: semanticFormatterCallback, - onChanged: state.enabled - ? (values) { - field.didChange(values); - } - : null, - ), - Row( - children: [ - if (displayValues != DisplayValues.none && - displayValues != DisplayValues.current) - minValueWidget - ?.call(effectiveNumberFormat.format(min)) ?? - Text(effectiveNumberFormat.format(min)), - const Spacer(), - if (displayValues != DisplayValues.none && - displayValues != DisplayValues.minMax) - valueWidget?.call( - '${effectiveNumberFormat.format(field.value!.start)} - ${effectiveNumberFormat.format(field.value!.end)}') ?? - Text( - '${effectiveNumberFormat.format(field.value!.start)} - ${effectiveNumberFormat.format(field.value!.end)}'), - const Spacer(), - if (displayValues != DisplayValues.none && - displayValues != DisplayValues.current) - maxValueWidget - ?.call(effectiveNumberFormat.format(max)) ?? - Text(effectiveNumberFormat.format(max)), - ], - ), - ], - ), - ), - ), - ); - }); + }) : super( + builder: (FormFieldState field) { + final state = field as _FormBuilderRangeSliderState; + final effectiveNumberFormat = numberFormat ?? NumberFormat.compact(); + if (field.value == null || + field.value!.start < min || + field.value!.start > max || + field.value!.end < min || + field.value!.end > max) { + if (initialValue == null) { + field.setValue(RangeValues(min, min)); + } else { + field.setValue( + RangeValues(initialValue.start, initialValue.end), + ); + } + } + // TODO: Solve focus issue when Flutter team solve this issue + // https://github.com/flutter/flutter/issues/53958 + return Focus( + focusNode: state.effectiveFocusNode, + skipTraversal: true, + canRequestFocus: state.enabled, + debugLabel: 'FormBuilderRangeSlider-$name', + child: InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: Container( + padding: const EdgeInsets.only(top: 10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RangeSlider( + values: field.value!, + min: min, + max: max, + divisions: divisions, + activeColor: activeColor, + inactiveColor: inactiveColor, + onChangeEnd: onChangeEnd, + onChangeStart: onChangeStart, + labels: labels, + semanticFormatterCallback: semanticFormatterCallback, + onChanged: + state.enabled + ? (values) { + field.didChange(values); + } + : null, + ), + Row( + children: [ + if (displayValues != DisplayValues.none && + displayValues != DisplayValues.current) + minValueWidget?.call( + effectiveNumberFormat.format(min), + ) ?? + Text(effectiveNumberFormat.format(min)), + const Spacer(), + if (displayValues != DisplayValues.none && + displayValues != DisplayValues.minMax) + valueWidget?.call( + '${effectiveNumberFormat.format(field.value!.start)} - ${effectiveNumberFormat.format(field.value!.end)}', + ) ?? + Text( + '${effectiveNumberFormat.format(field.value!.start)} - ${effectiveNumberFormat.format(field.value!.end)}', + ), + const Spacer(), + if (displayValues != DisplayValues.none && + displayValues != DisplayValues.current) + maxValueWidget?.call( + effectiveNumberFormat.format(max), + ) ?? + Text(effectiveNumberFormat.format(max)), + ], + ), + ], + ), + ), + ), + ); + }, + ); @override FormBuilderFieldDecorationState - createState() => _FormBuilderRangeSliderState(); + createState() => _FormBuilderRangeSliderState(); } -class _FormBuilderRangeSliderState extends FormBuilderFieldDecorationState< - FormBuilderRangeSlider, RangeValues> {} +class _FormBuilderRangeSliderState + extends + FormBuilderFieldDecorationState {} diff --git a/lib/src/fields/form_builder_slider.dart b/lib/src/fields/form_builder_slider.dart index ab0799612..e394d1105 100644 --- a/lib/src/fields/form_builder_slider.dart +++ b/lib/src/fields/form_builder_slider.dart @@ -212,65 +212,68 @@ class FormBuilderSlider extends FormBuilderFieldDecoration { this.minValueWidget, this.valueWidget, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderSliderState; - final effectiveNumberFormat = - numberFormat ?? NumberFormat.compact(); + builder: (FormFieldState field) { + final state = field as _FormBuilderSliderState; + final effectiveNumberFormat = numberFormat ?? NumberFormat.compact(); - return InputDecorator( - decoration: state.decoration, - child: Container( - padding: const EdgeInsets.only(top: 10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Slider( - value: field.value!, - min: min, - max: max, - divisions: divisions, - activeColor: activeColor, - inactiveColor: inactiveColor, - onChangeEnd: onChangeEnd, - onChangeStart: onChangeStart, - label: label, - semanticFormatterCallback: semanticFormatterCallback, - onChanged: state.enabled - ? (value) { - field.didChange(value); - } - : null, - autofocus: autofocus, - mouseCursor: mouseCursor, - focusNode: state.effectiveFocusNode, - ), - Row( - children: [ - if (displayValues != DisplayValues.none && - displayValues != DisplayValues.current) - minValueWidget - ?.call(effectiveNumberFormat.format(min)) ?? - Text(effectiveNumberFormat.format(min)), - const Spacer(), - if (displayValues != DisplayValues.none && - displayValues != DisplayValues.minMax) - valueWidget?.call( - effectiveNumberFormat.format(field.value)) ?? - Text(effectiveNumberFormat.format(field.value)), - const Spacer(), - if (displayValues != DisplayValues.none && - displayValues != DisplayValues.current) - maxValueWidget - ?.call(effectiveNumberFormat.format(max)) ?? - Text(effectiveNumberFormat.format(max)), - ], - ), - ], - ), - ), - ); - }, - ); + return InputDecorator( + decoration: state.decoration, + child: Container( + padding: const EdgeInsets.only(top: 10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Slider( + value: field.value!, + min: min, + max: max, + divisions: divisions, + activeColor: activeColor, + inactiveColor: inactiveColor, + onChangeEnd: onChangeEnd, + onChangeStart: onChangeStart, + label: label, + semanticFormatterCallback: semanticFormatterCallback, + onChanged: + state.enabled + ? (value) { + field.didChange(value); + } + : null, + autofocus: autofocus, + mouseCursor: mouseCursor, + focusNode: state.effectiveFocusNode, + ), + Row( + children: [ + if (displayValues != DisplayValues.none && + displayValues != DisplayValues.current) + minValueWidget?.call( + effectiveNumberFormat.format(min), + ) ?? + Text(effectiveNumberFormat.format(min)), + const Spacer(), + if (displayValues != DisplayValues.none && + displayValues != DisplayValues.minMax) + valueWidget?.call( + effectiveNumberFormat.format(field.value), + ) ?? + Text(effectiveNumberFormat.format(field.value)), + const Spacer(), + if (displayValues != DisplayValues.none && + displayValues != DisplayValues.current) + maxValueWidget?.call( + effectiveNumberFormat.format(max), + ) ?? + Text(effectiveNumberFormat.format(max)), + ], + ), + ], + ), + ), + ); + }, + ); @override FormBuilderFieldDecorationState createState() => diff --git a/lib/src/fields/form_builder_switch.dart b/lib/src/fields/form_builder_switch.dart index e83697704..fa7b0520f 100644 --- a/lib/src/fields/form_builder_switch.dart +++ b/lib/src/fields/form_builder_switch.dart @@ -117,39 +117,40 @@ class FormBuilderSwitch extends FormBuilderFieldDecoration { this.autofocus = false, this.selected = false, }) : super( - builder: (FormFieldState field) { - final state = field as _FormBuilderSwitchState; - - return InputDecorator( - decoration: state.decoration, - isFocused: state.effectiveFocusNode.hasFocus, - child: SwitchListTile( - focusNode: state.effectiveFocusNode, - dense: true, - isThreeLine: false, - contentPadding: contentPadding, - title: title, - value: state.value ?? false, - onChanged: state.enabled - ? (value) { - field.didChange(value); - } - : null, - activeColor: activeColor, - activeThumbImage: activeThumbImage, - activeTrackColor: activeTrackColor, - inactiveThumbColor: inactiveThumbColor, - inactiveThumbImage: activeThumbImage, - inactiveTrackColor: inactiveTrackColor, - secondary: secondary, - subtitle: subtitle, - autofocus: autofocus, - selected: selected, - controlAffinity: controlAffinity, - ), - ); - }, - ); + builder: (FormFieldState field) { + final state = field as _FormBuilderSwitchState; + + return InputDecorator( + decoration: state.decoration, + isFocused: state.effectiveFocusNode.hasFocus, + child: SwitchListTile( + focusNode: state.effectiveFocusNode, + dense: true, + isThreeLine: false, + contentPadding: contentPadding, + title: title, + value: state.value ?? false, + onChanged: + state.enabled + ? (value) { + field.didChange(value); + } + : null, + activeColor: activeColor, + activeThumbImage: activeThumbImage, + activeTrackColor: activeTrackColor, + inactiveThumbColor: inactiveThumbColor, + inactiveThumbImage: activeThumbImage, + inactiveTrackColor: inactiveTrackColor, + secondary: secondary, + subtitle: subtitle, + autofocus: autofocus, + selected: selected, + controlAffinity: controlAffinity, + ), + ); + }, + ); @override FormBuilderFieldDecorationState createState() => diff --git a/lib/src/fields/form_builder_text_field.dart b/lib/src/fields/form_builder_text_field.dart index fd3ad8bb4..4568ce200 100644 --- a/lib/src/fields/form_builder_text_field.dart +++ b/lib/src/fields/form_builder_text_field.dart @@ -331,6 +331,9 @@ class FormBuilderTextField extends FormBuilderFieldDecoration { /// enabled, [onTap] is called for every tap including consecutive taps. final bool onTapAlwaysCalled; + /// {@macro flutter.widgets.editableText.stylusHandwritingEnabled} + final bool stylusHandwritingEnabled; + /// {@macro flutter.widgets.editableText.scribbleEnabled} final bool scribbleEnabled; @@ -436,96 +439,104 @@ class FormBuilderTextField extends FormBuilderFieldDecoration { this.groupId = EditableText, this.onAppPrivateCommand, this.onTapAlwaysCalled = false, + @Deprecated( + 'Use `stylusHandwritingEnabled` instead. ' + 'This feature was deprecated after v3.27.0-0.2.pre.', + ) this.scribbleEnabled = true, + this.stylusHandwritingEnabled = + EditableText.defaultStylusHandwritingEnabled, this.selectionControls, this.statesController, this.undoController, - }) : assert(initialValue == null || controller == null), - assert(minLines == null || minLines > 0), - assert(maxLines == null || maxLines > 0), - assert( - (minLines == null) || (maxLines == null) || (maxLines >= minLines), - 'minLines can\'t be greater than maxLines', - ), - assert( - !expands || (minLines == null && maxLines == null), - 'minLines and maxLines must be null when expands is true.', - ), - assert(!obscureText || maxLines == 1, - 'Obscured fields cannot be multiline.'), - assert(maxLength == null || maxLength > 0), - super( - initialValue: controller != null ? controller.text : initialValue, - builder: (FormFieldState field) { - final state = field as _FormBuilderTextFieldState; - - return TextField( - restorationId: restorationId, - controller: state._effectiveController, - focusNode: state.effectiveFocusNode, - decoration: state.decoration, - keyboardType: keyboardType, - textInputAction: textInputAction, - style: style, - strutStyle: strutStyle, - textAlign: textAlign, - textAlignVertical: textAlignVertical, - textDirection: textDirection, - textCapitalization: textCapitalization, - autofocus: autofocus, - readOnly: readOnly, - showCursor: showCursor, - obscureText: obscureText, - autocorrect: autocorrect, - enableSuggestions: enableSuggestions, - maxLengthEnforcement: maxLengthEnforcement, - maxLines: maxLines, - minLines: minLines, - expands: expands, - maxLength: maxLength, - onTap: onTap, - onTapOutside: onTapOutside, - onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, - inputFormatters: inputFormatters, - enabled: state.enabled, - cursorWidth: cursorWidth, - cursorHeight: cursorHeight, - cursorRadius: cursorRadius, - cursorColor: cursorColor, - scrollPadding: scrollPadding, - keyboardAppearance: keyboardAppearance, - enableInteractiveSelection: enableInteractiveSelection, - buildCounter: buildCounter, - dragStartBehavior: dragStartBehavior, - scrollController: scrollController, - scrollPhysics: scrollPhysics, - selectionHeightStyle: selectionHeightStyle, - selectionWidthStyle: selectionWidthStyle, - smartDashesType: smartDashesType, - smartQuotesType: smartQuotesType, - mouseCursor: mouseCursor, - contextMenuBuilder: contextMenuBuilder, - obscuringCharacter: obscuringCharacter, - autofillHints: autofillHints, - magnifierConfiguration: magnifierConfiguration, - contentInsertionConfiguration: contentInsertionConfiguration, - spellCheckConfiguration: spellCheckConfiguration, - clipBehavior: clipBehavior, - canRequestFocus: canRequestFocus, - cursorErrorColor: cursorErrorColor, - cursorOpacityAnimates: cursorOpacityAnimates, - enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, - groupId: groupId, - onAppPrivateCommand: onAppPrivateCommand, - onTapAlwaysCalled: onTapAlwaysCalled, - scribbleEnabled: scribbleEnabled, - selectionControls: selectionControls, - statesController: statesController, - undoController: undoController, - ); - }, - ); + }) : assert(initialValue == null || controller == null), + assert(minLines == null || minLines > 0), + assert(maxLines == null || maxLines > 0), + assert( + (minLines == null) || (maxLines == null) || (maxLines >= minLines), + 'minLines can\'t be greater than maxLines', + ), + assert( + !expands || (minLines == null && maxLines == null), + 'minLines and maxLines must be null when expands is true.', + ), + assert( + !obscureText || maxLines == 1, + 'Obscured fields cannot be multiline.', + ), + assert(maxLength == null || maxLength > 0), + super( + initialValue: controller != null ? controller.text : initialValue, + builder: (FormFieldState field) { + final state = field as _FormBuilderTextFieldState; + + return TextField( + restorationId: restorationId, + controller: state._effectiveController, + focusNode: state.effectiveFocusNode, + decoration: state.decoration, + keyboardType: keyboardType, + textInputAction: textInputAction, + style: style, + strutStyle: strutStyle, + textAlign: textAlign, + textAlignVertical: textAlignVertical, + textDirection: textDirection, + textCapitalization: textCapitalization, + autofocus: autofocus, + readOnly: readOnly, + showCursor: showCursor, + obscureText: obscureText, + autocorrect: autocorrect, + enableSuggestions: enableSuggestions, + maxLengthEnforcement: maxLengthEnforcement, + maxLines: maxLines, + minLines: minLines, + expands: expands, + maxLength: maxLength, + onTap: onTap, + onTapOutside: onTapOutside, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmitted, + inputFormatters: inputFormatters, + enabled: state.enabled, + cursorWidth: cursorWidth, + cursorHeight: cursorHeight, + cursorRadius: cursorRadius, + cursorColor: cursorColor, + scrollPadding: scrollPadding, + keyboardAppearance: keyboardAppearance, + enableInteractiveSelection: enableInteractiveSelection, + buildCounter: buildCounter, + dragStartBehavior: dragStartBehavior, + scrollController: scrollController, + scrollPhysics: scrollPhysics, + selectionHeightStyle: selectionHeightStyle, + selectionWidthStyle: selectionWidthStyle, + smartDashesType: smartDashesType, + smartQuotesType: smartQuotesType, + mouseCursor: mouseCursor, + contextMenuBuilder: contextMenuBuilder, + obscuringCharacter: obscuringCharacter, + autofillHints: autofillHints, + magnifierConfiguration: magnifierConfiguration, + contentInsertionConfiguration: contentInsertionConfiguration, + spellCheckConfiguration: spellCheckConfiguration, + clipBehavior: clipBehavior, + canRequestFocus: canRequestFocus, + cursorErrorColor: cursorErrorColor, + cursorOpacityAnimates: cursorOpacityAnimates, + enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, + groupId: groupId, + onAppPrivateCommand: onAppPrivateCommand, + onTapAlwaysCalled: onTapAlwaysCalled, + stylusHandwritingEnabled: stylusHandwritingEnabled, + selectionControls: selectionControls, + statesController: statesController, + undoController: undoController, + ); + }, + ); static Widget _defaultContextMenuBuilder( BuildContext context, diff --git a/lib/src/form_builder.dart b/lib/src/form_builder.dart index 6bfc961b0..f5ce4239a 100644 --- a/lib/src/form_builder.dart +++ b/lib/src/form_builder.dart @@ -118,8 +118,8 @@ class FormBuilder extends StatefulWidget { } /// A type alias for a map of form fields. -typedef FormBuilderFields - = Map, dynamic>>; +typedef FormBuilderFields = + Map, dynamic>>; class FormBuilderState extends State { final GlobalKey _formKey = GlobalKey(); @@ -153,10 +153,11 @@ class FormBuilderState extends State { /// Get a map of errors Map get errors => { - for (var element - in fields.entries.where((element) => element.value.hasError)) - element.key.toString(): element.value.errorText ?? '' - }; + for (var element in fields.entries.where( + (element) => element.value.hasError, + )) + element.key.toString(): element.value.errorText ?? '', + }; /// Get initialValue. Map get initialValue => widget.initialValue; @@ -166,23 +167,23 @@ class FormBuilderState extends State { /// Get all fields values of form. Map get instantValue => Map.unmodifiable( - _instantValue.map( - (key, value) => MapEntry( - key, - _transformers[key] == null ? value : _transformers[key]!(value), - ), - ), - ); + _instantValue.map( + (key, value) => MapEntry( + key, + _transformers[key] == null ? value : _transformers[key]!(value), + ), + ), + ); /// Returns the saved value only Map get value => Map.unmodifiable( - _savedValue.map( - (key, value) => MapEntry( - key, - _transformers[key] == null ? value : _transformers[key]!(value), - ), - ), - ); + _savedValue.map( + (key, value) => MapEntry( + key, + _transformers[key] == null ? value : _transformers[key]!(value), + ), + ), + ); dynamic transformValue(String name, T? v) { final t = _transformers[name]; @@ -220,8 +221,10 @@ class FormBuilderState extends State { final oldField = _fields[name]; assert(() { if (oldField != null) { - debugPrint('Warning! Replacing duplicate Field for $name' - ' -- this is OK to ignore as long as the field was intentionally replaced'); + debugPrint( + 'Warning! Replacing duplicate Field for $name' + ' -- this is OK to ignore as long as the field was intentionally replaced', + ); } return true; }()); @@ -233,10 +236,7 @@ class FormBuilderState extends State { _instantValue[name] = field.initialValue ?? initialValue[name]; } - field.setValue( - _instantValue[name], - populateForm: false, - ); + field.setValue(_instantValue[name], populateForm: false); } /// Unregister a field on form @@ -261,8 +261,10 @@ class FormBuilderState extends State { assert(() { // This is OK to ignore when you are intentionally replacing a field // with another field using the same name. - debugPrint('Warning! Ignoring Field unregistration for $name' - ' -- this is OK to ignore as long as the field was intentionally replaced'); + debugPrint( + 'Warning! Ignoring Field unregistration for $name' + ' -- this is OK to ignore as long as the field was intentionally replaced', + ); return true; }()); } @@ -369,9 +371,7 @@ class FormBuilderState extends State { // `onChanged` is called during setInternalFieldValue else will be called early child: _FormBuilderScope( formState: this, - child: FocusTraversalGroup( - child: widget.child, - ), + child: FocusTraversalGroup(child: widget.child), ), ); } diff --git a/lib/src/form_builder_field.dart b/lib/src/form_builder_field.dart index c90f373ce..588f65a7f 100644 --- a/lib/src/form_builder_field.dart +++ b/lib/src/form_builder_field.dart @@ -89,25 +89,23 @@ class FormBuilderFieldState, T> /// initialValue prevails. T? get initialValue => widget.initialValue ?? - (_formBuilderState?.initialValue ?? - const {})[widget.name] as T?; + (_formBuilderState?.initialValue ?? const {})[widget + .name] + as T?; dynamic get transformedValue => widget.valueTransformer == null ? value : widget.valueTransformer!(value); @override - /// Returns the current error text, /// which may be a validation error or a custom error text. String? get errorText => super.errorText ?? _customErrorText; @override - /// Returns `true` if the field has an error or has a custom error text. bool get hasError => super.hasError || errorText != null; @override - /// Returns `true` if the field is valid and has no custom error text. bool get isValid => super.isValid && _customErrorText == null; @@ -219,7 +217,6 @@ class FormBuilderFieldState, T> } @override - /// Reset field value to initial value /// /// Also reset custom error text if exists, and set [isDirty] to `false`. @@ -259,7 +256,8 @@ class FormBuilderFieldState, T> } final isValid = super.validate() && !hasError; - final fields = _formBuilderState?.fields ?? + final fields = + _formBuilderState?.fields ?? , dynamic>>{}; if (!isValid && diff --git a/lib/src/form_builder_field_decoration.dart b/lib/src/form_builder_field_decoration.dart index 135afba0c..bff8e0fd9 100644 --- a/lib/src/form_builder_field_decoration.dart +++ b/lib/src/form_builder_field_decoration.dart @@ -22,34 +22,38 @@ class FormBuilderFieldDecoration extends FormBuilderField { required super.builder, this.decoration = const InputDecoration(), }) : assert( - decoration.enabled == enabled || - (enabled == false && decoration.enabled), - '''decoration.enabled will be used instead of enabled FormBuilderField property. + decoration.enabled == enabled || + (enabled == false && decoration.enabled), + '''decoration.enabled will be used instead of enabled FormBuilderField property. This will create conflicts and unexpected behaviors on focus, errorText, and other properties. - Please, to enable or disable the field, use the enabled property of FormBuilderField.'''); + Please, to enable or disable the field, use the enabled property of FormBuilderField.''', + ); final InputDecoration decoration; @override FormBuilderFieldDecorationState, T> - createState() => - FormBuilderFieldDecorationState, T>(); + createState() => + FormBuilderFieldDecorationState, T>(); } -class FormBuilderFieldDecorationState, - T> extends FormBuilderFieldState, T> { +class FormBuilderFieldDecorationState< + F extends FormBuilderFieldDecoration, + T +> + extends FormBuilderFieldState, T> { @override F get widget => super.widget as F; /// Get the decoration with the current state InputDecoration get decoration => widget.decoration.copyWith( - // Read only allow show error to support property skipDisabled - errorText: widget.enabled || readOnly + // Read only allow show error to support property skipDisabled + errorText: + widget.enabled || readOnly ? widget.decoration.errorText ?? errorText : null, - enabled: widget.decoration.enabled - ? widget.enabled - : widget.decoration.enabled, - ); + enabled: + widget.decoration.enabled ? widget.enabled : widget.decoration.enabled, + ); @override bool get hasError => super.hasError || widget.decoration.errorText != null; diff --git a/lib/src/form_builder_field_option.dart b/lib/src/form_builder_field_option.dart index 1a68f9e00..95e41994d 100644 --- a/lib/src/form_builder_field_option.dart +++ b/lib/src/form_builder_field_option.dart @@ -12,11 +12,7 @@ class FormBuilderFieldOption extends StatelessWidget { final T value; /// Creates an option for fields with selection options - const FormBuilderFieldOption({ - super.key, - required this.value, - this.child, - }); + const FormBuilderFieldOption({super.key, required this.value, this.child}); @override Widget build(BuildContext context) { diff --git a/lib/src/widgets/grouped_checkbox.dart b/lib/src/widgets/grouped_checkbox.dart index 5c2f50d13..28a216fa9 100644 --- a/lib/src/widgets/grouped_checkbox.dart +++ b/lib/src/widgets/grouped_checkbox.dart @@ -229,37 +229,38 @@ class GroupedCheckbox extends StatelessWidget { return switch (orientation) { OptionsOrientation.auto => OverflowBar( - alignment: MainAxisAlignment.spaceEvenly, - children: widgetList, - ), + alignment: MainAxisAlignment.spaceEvenly, + children: widgetList, + ), OptionsOrientation.vertical => SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: widgetList, - ), + scrollDirection: Axis.vertical, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: widgetList, ), + ), OptionsOrientation.horizontal => SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: widgetList.map((item) { - return Column(children: [item]); - }).toList(), - ), + scrollDirection: Axis.horizontal, + child: Row( + children: + widgetList.map((item) { + return Column(children: [item]); + }).toList(), ), + ), OptionsOrientation.wrap => SingleChildScrollView( - child: Wrap( - spacing: wrapSpacing, - runSpacing: wrapRunSpacing, - textDirection: wrapTextDirection, - crossAxisAlignment: wrapCrossAxisAlignment, - verticalDirection: wrapVerticalDirection, - alignment: wrapAlignment, - direction: Axis.horizontal, - runAlignment: wrapRunAlignment, - children: widgetList, - ), - ) + child: Wrap( + spacing: wrapSpacing, + runSpacing: wrapRunSpacing, + textDirection: wrapTextDirection, + crossAxisAlignment: wrapCrossAxisAlignment, + verticalDirection: wrapVerticalDirection, + alignment: wrapAlignment, + direction: Axis.horizontal, + runAlignment: wrapRunAlignment, + children: widgetList, + ), + ), }; } @@ -275,30 +276,35 @@ class GroupedCheckbox extends StatelessWidget { focusColor: focusColor, hoverColor: hoverColor, materialTapTargetSize: materialTapTargetSize, - value: tristate - ? value?.contains(optionValue) - : true == value?.contains(optionValue), + value: + tristate + ? value?.contains(optionValue) + : true == value?.contains(optionValue), tristate: tristate, - onChanged: isOptionDisabled - ? null - : (selected) { - List selectedListItems = value == null ? [] : List.of(value!); - selected! - ? selectedListItems.add(optionValue) - : selectedListItems.remove(optionValue); - onChanged(selectedListItems); - }, + onChanged: + isOptionDisabled + ? null + : (selected) { + List selectedListItems = + value == null ? [] : List.of(value!); + selected! + ? selectedListItems.add(optionValue) + : selectedListItems.remove(optionValue); + onChanged(selectedListItems); + }, ); final label = GestureDetector( - onTap: isOptionDisabled - ? null - : () { - List selectedListItems = value == null ? [] : List.of(value!); - selectedListItems.contains(optionValue) - ? selectedListItems.remove(optionValue) - : selectedListItems.add(optionValue); - onChanged(selectedListItems); - }, + onTap: + isOptionDisabled + ? null + : () { + List selectedListItems = + value == null ? [] : List.of(value!); + selectedListItems.contains(optionValue) + ? selectedListItems.remove(optionValue) + : selectedListItems.add(optionValue); + onChanged(selectedListItems); + }, child: option, ); diff --git a/lib/src/widgets/grouped_radio.dart b/lib/src/widgets/grouped_radio.dart index aaaf642b1..f990cd662 100644 --- a/lib/src/widgets/grouped_radio.dart +++ b/lib/src/widgets/grouped_radio.dart @@ -219,33 +219,33 @@ class _GroupedRadioState extends State> { return switch (widget.orientation) { OptionsOrientation.auto => OverflowBar( - alignment: MainAxisAlignment.spaceEvenly, - children: widgetList, - ), + alignment: MainAxisAlignment.spaceEvenly, + children: widgetList, + ), OptionsOrientation.vertical => SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: widgetList, - ), + scrollDirection: Axis.vertical, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: widgetList, ), + ), OptionsOrientation.horizontal => SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row(children: widgetList), - ), + scrollDirection: Axis.horizontal, + child: Row(children: widgetList), + ), OptionsOrientation.wrap => SingleChildScrollView( - child: Wrap( - spacing: widget.wrapSpacing, - runSpacing: widget.wrapRunSpacing, - textDirection: widget.wrapTextDirection, - crossAxisAlignment: widget.wrapCrossAxisAlignment, - verticalDirection: widget.wrapVerticalDirection, - alignment: widget.wrapAlignment, - direction: Axis.horizontal, - runAlignment: widget.wrapRunAlignment, - children: widgetList, - ), - ) + child: Wrap( + spacing: widget.wrapSpacing, + runSpacing: widget.wrapRunSpacing, + textDirection: widget.wrapTextDirection, + crossAxisAlignment: widget.wrapCrossAxisAlignment, + verticalDirection: widget.wrapVerticalDirection, + alignment: widget.wrapAlignment, + direction: Axis.horizontal, + runAlignment: widget.wrapRunAlignment, + children: widgetList, + ), + ), }; } @@ -261,19 +261,21 @@ class _GroupedRadioState extends State> { hoverColor: widget.hoverColor, materialTapTargetSize: widget.materialTapTargetSize, value: optionValue, - onChanged: isOptionDisabled - ? null - : (T? selected) { - widget.onChanged(selected); - }, + onChanged: + isOptionDisabled + ? null + : (T? selected) { + widget.onChanged(selected); + }, ); final label = GestureDetector( - onTap: isOptionDisabled - ? null - : () { - widget.onChanged(optionValue); - }, + onTap: + isOptionDisabled + ? null + : () { + widget.onChanged(optionValue); + }, child: option, ); @@ -304,12 +306,14 @@ class _GroupedRadioState extends State> { compositeItem = Container( decoration: widget.itemDecoration, margin: EdgeInsets.only( - bottom: widget.orientation == OptionsOrientation.vertical - ? widget.wrapSpacing - : 0.0, - right: widget.orientation == OptionsOrientation.horizontal - ? widget.wrapSpacing - : 0.0, + bottom: + widget.orientation == OptionsOrientation.vertical + ? widget.wrapSpacing + : 0.0, + right: + widget.orientation == OptionsOrientation.horizontal + ? widget.wrapSpacing + : 0.0, ), child: compositeItem, ); diff --git a/pubspec.lock b/pubspec.lock index 3b55fffaa..0fe23277c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -79,18 +79,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -103,18 +103,18 @@ packages: dependency: transitive description: name: lints - sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.1" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -127,18 +127,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -148,50 +148,50 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" vector_math: dependency: transitive description: @@ -204,10 +204,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.3.1" sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.27.0" + dart: ">=3.7.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5e3ed7558..6d792c103 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_form_builder description: This package helps in creation of forms in Flutter by removing the boilerplate code, reusing validation, react to changes, and collect final user input. -version: 10.0.0-dev.3 +version: 10.0.0 repository: https://github.com/flutter-form-builder-ecosystem/flutter_form_builder issue_tracker: https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues homepage: https://github.com/flutter-form-builder-ecosystem @@ -10,8 +10,8 @@ funding: - https://opencollective.com/flutter-form-builder-ecosystem environment: - sdk: ">=3.6.0 <4.0.0" - flutter: ">=3.27.0" + sdk: ">=3.7.0 <4.0.0" + flutter: ">=3.29.0" dependencies: flutter: diff --git a/test/src/fields/form_builder_checkbox_group_test.dart b/test/src/fields/form_builder_checkbox_group_test.dart index 57fd22a29..2a2d455db 100644 --- a/test/src/fields/form_builder_checkbox_group_test.dart +++ b/test/src/fields/form_builder_checkbox_group_test.dart @@ -41,8 +41,9 @@ void main() { FormBuilderFieldOption(key: ValueKey('1'), value: 1), FormBuilderFieldOption(key: ValueKey('2'), value: 2), ], - itemDecoration: - BoxDecoration(border: Border.all(color: Colors.blueAccent)), + itemDecoration: BoxDecoration( + border: Border.all(color: Colors.blueAccent), + ), ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -66,8 +67,9 @@ void main() { FormBuilderFieldOption(key: ValueKey('1'), value: 1), FormBuilderFieldOption(key: ValueKey('2'), value: 2), ], - itemDecoration: - BoxDecoration(border: Border.all(color: Colors.blueAccent)), + itemDecoration: BoxDecoration( + border: Border.all(color: Colors.blueAccent), + ), ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -137,29 +139,36 @@ void main() { expect(formSave(), isTrue); expect(formValue(fieldName), [1, 3]); - Checkbox checkbox1 = tester - .element(find.byKey(const ValueKey('1'))) - .findAncestorWidgetOfExactType()! - .children - .first as Checkbox; - Checkbox checkbox2 = tester - .element(find.byKey(const ValueKey('2'))) - .findAncestorWidgetOfExactType()! - .children - .first as Checkbox; - Checkbox checkbox3 = tester - .element(find.byKey(const ValueKey('3'))) - .findAncestorWidgetOfExactType()! - .children - .first as Checkbox; + Checkbox checkbox1 = + tester + .element(find.byKey(const ValueKey('1'))) + .findAncestorWidgetOfExactType()! + .children + .first + as Checkbox; + Checkbox checkbox2 = + tester + .element(find.byKey(const ValueKey('2'))) + .findAncestorWidgetOfExactType()! + .children + .first + as Checkbox; + Checkbox checkbox3 = + tester + .element(find.byKey(const ValueKey('3'))) + .findAncestorWidgetOfExactType()! + .children + .first + as Checkbox; // checkboxes should represent the state of the didChange value expect(checkbox1.value, true); expect(checkbox2.value, false); expect(checkbox3.value, true); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final testWidget = FormBuilderCheckboxGroup( name: widgetName, diff --git a/test/src/fields/form_builder_checkbox_test.dart b/test/src/fields/form_builder_checkbox_test.dart index f08103c8f..018ffb953 100644 --- a/test/src/fields/form_builder_checkbox_test.dart +++ b/test/src/fields/form_builder_checkbox_test.dart @@ -28,8 +28,9 @@ void main() { expect(formSave(), isTrue); expect(formValue(widgetName), isFalse); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final testWidget = FormBuilderCheckbox( name: widgetName, diff --git a/test/src/fields/form_builder_choice_chips_test.dart b/test/src/fields/form_builder_choice_chips_test.dart index 8bf4a68a3..a5ebff045 100644 --- a/test/src/fields/form_builder_choice_chips_test.dart +++ b/test/src/fields/form_builder_choice_chips_test.dart @@ -45,10 +45,9 @@ void main() { FormBuilderChipOption(key: ValueKey('3'), value: 3), ], ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: {widgetName: 1}, - )); + await tester.pumpWidget( + buildTestableFieldWidget(testWidget, initialValue: {widgetName: 1}), + ); await tester.ensureVisible(find.byKey(const ValueKey('1'))); expect(formInstantValue(widgetName), equals(1)); @@ -84,8 +83,9 @@ void main() { expect(formValue(widgetName), equals(3)); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final testWidget = FormBuilderChoiceChips( name: widgetName, diff --git a/test/src/fields/form_builder_date_range_picker_test.dart b/test/src/fields/form_builder_date_range_picker_test.dart index 479ba8229..58c62ce10 100644 --- a/test/src/fields/form_builder_date_range_picker_test.dart +++ b/test/src/fields/form_builder_date_range_picker_test.dart @@ -40,10 +40,9 @@ void main() { expect(formSave(), isTrue); expect( formValue(widgetName), - equals(DateTimeRange( - start: DateTime(2010, 1, 2), - end: DateTime(2010, 1, 4), - )), + equals( + DateTimeRange(start: DateTime(2010, 1, 2), end: DateTime(2010, 1, 4)), + ), ); }); @@ -63,15 +62,15 @@ void main() { expect(formSave(), isTrue); expect( formValue(widgetName), - equals(DateTimeRange( - start: DateTime(2010, 1, 2), - end: DateTime(2010, 1, 4), - )), + equals( + DateTimeRange(start: DateTime(2010, 1, 2), end: DateTime(2010, 1, 4)), + ), ); }); - testWidgets('text field empty when value is null', - (WidgetTester tester) async { + testWidgets('text field empty when value is null', ( + WidgetTester tester, + ) async { const widgetName = 'formBuilderDateRangePicker'; final testWidget = FormBuilderDateRangePicker( name: widgetName, @@ -103,8 +102,9 @@ void main() { ); }); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; const saveText = 'SAVE THE DATE'; final testWidget = FormBuilderDateRangePicker( @@ -136,8 +136,9 @@ void main() { await tester.pumpAndSettle(); expect(find.text(saveText), findsOneWidget); }); - testWidgets('When press clear button then clear value text field', - (WidgetTester tester) async { + testWidgets('When press clear button then clear value text field', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final initialValue = DateTimeRange( start: DateTime(2010, 1, 2), diff --git a/test/src/fields/form_builder_date_time_picker_test.dart b/test/src/fields/form_builder_date_time_picker_test.dart index 0ad9439fa..5555a952d 100644 --- a/test/src/fields/form_builder_date_time_picker_test.dart +++ b/test/src/fields/form_builder_date_time_picker_test.dart @@ -38,35 +38,41 @@ void main() { await tester.pumpAndSettle(); expect(formSave(), isTrue); - expect(formValue(widgetName), - DateTime(dateNow.year, dateNow.month, testDay, 12)); + expect( + formValue(widgetName), + DateTime(dateNow.year, dateNow.month, testDay, 12), + ); }); testWidgets( - 'should change to text field and show keyboard when edit icon is pressed', - (WidgetTester tester) async { - const widgetName = 'fdtp3'; - final widgetKey = UniqueKey(); - const keyboardType = TextInputType.datetime; + 'should change to text field and show keyboard when edit icon is pressed', + (WidgetTester tester) async { + const widgetName = 'fdtp3'; + final widgetKey = UniqueKey(); + const keyboardType = TextInputType.datetime; - final testWidget = FormBuilderDateTimePicker( - key: widgetKey, - name: widgetName, - keyboardType: keyboardType, - inputType: InputType.date, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - await tester.tap(find.byKey(widgetKey)); - await tester.pumpAndSettle(); + final testWidget = FormBuilderDateTimePicker( + key: widgetKey, + name: widgetName, + keyboardType: keyboardType, + inputType: InputType.date, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + await tester.tap(find.byKey(widgetKey)); + await tester.pumpAndSettle(); - // change to input edition - await tester.tap(find.byIcon(Icons.edit_outlined)); - await tester.pumpAndSettle(); + // change to input edition + await tester.tap(find.byIcon(Icons.edit_outlined)); + await tester.pumpAndSettle(); - final textField = tester.widget(find.byType(TextField).first); - expect(textField.keyboardType, equals(keyboardType)); - }); - testWidgets('should show a past year when set on lastDate', - (WidgetTester tester) async { + final textField = tester.widget( + find.byType(TextField).first, + ); + expect(textField.keyboardType, equals(keyboardType)); + }, + ); + testWidgets('should show a past year when set on lastDate', ( + WidgetTester tester, + ) async { const widgetName = 'fdtp3'; final widgetKey = UniqueKey(); const confirmText = 'OK'; @@ -104,10 +110,12 @@ void main() { cancelText: cancelText, ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: {widgetName: dateFuture}, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + initialValue: {widgetName: dateFuture}, + ), + ); expect(formSave(), isTrue); expect(formValue(widgetName), dateFuture); @@ -128,8 +136,15 @@ void main() { expect(formSave(), isTrue); expect( formValue(widgetName), - DateTime(dateFuture.year, dateFuture.month, testDay, dateFuture.hour, - dateFuture.minute, 0, 0), + DateTime( + dateFuture.year, + dateFuture.month, + testDay, + dateFuture.hour, + dateFuture.minute, + 0, + 0, + ), ); }); testWidgets('to Widget', (WidgetTester tester) async { @@ -168,15 +183,23 @@ void main() { expect(formSave(), isTrue); expect( formValue(widgetName), - DateTime(datePast.year, datePast.month, testDay, datePast.hour, - datePast.minute, 0, 0), + DateTime( + datePast.year, + datePast.month, + testDay, + datePast.hour, + datePast.minute, + 0, + 0, + ), ); }); }); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final widgetKey = UniqueKey(); const confirmText = 'OK'; diff --git a/test/src/fields/form_builder_dropdown_test.dart b/test/src/fields/form_builder_dropdown_test.dart index 293399158..4f29a3832 100644 --- a/test/src/fields/form_builder_dropdown_test.dart +++ b/test/src/fields/form_builder_dropdown_test.dart @@ -12,18 +12,9 @@ void main() { final testWidget = FormBuilderDropdown( name: widgetName, items: const [ - DropdownMenuItem( - value: 1, - child: Text('One'), - ), - DropdownMenuItem( - value: 2, - child: Text('Two'), - ), - DropdownMenuItem( - value: 3, - child: Text('Three'), - ), + DropdownMenuItem(value: 1, child: Text('One')), + DropdownMenuItem(value: 2, child: Text('Two')), + DropdownMenuItem(value: 3, child: Text('Three')), ], ); final widgetFinder = find.byWidget(testWidget); @@ -45,8 +36,9 @@ void main() { expect(formSave(), isTrue); expect(formValue(widgetName), equals(1)); }); - testWidgets('reset to initial value when update items', - (WidgetTester tester) async { + testWidgets('reset to initial value when update items', ( + WidgetTester tester, + ) async { const widgetName = 'dropdown_field'; const buttonKey = Key('update_button'); @@ -85,8 +77,9 @@ void main() { expect(formSave(), isTrue); expect(formValue(widgetName), equals(3)); }); - testWidgets('reset to initial value when update items with same values', - (WidgetTester tester) async { + testWidgets('reset to initial value when update items with same values', ( + WidgetTester tester, + ) async { const widgetName = 'dropdown_field'; const buttonKey = Key('update_button'); @@ -133,8 +126,9 @@ void main() { expect(formValue(widgetName), equals(1)); }); - testWidgets('reset to initial value when update items with same children', - (WidgetTester tester) async { + testWidgets('reset to initial value when update items with same children', ( + WidgetTester tester, + ) async { const widgetName = 'dropdown_field'; const buttonKey = Key('update_button'); const option1 = Text('Option 1'); @@ -183,8 +177,9 @@ void main() { expect(formSave(), isTrue); expect(formValue(widgetName), equals(3)); }); - testWidgets('maintain initial value when update to equals items', - (WidgetTester tester) async { + testWidgets('maintain initial value when update to equals items', ( + WidgetTester tester, + ) async { const widgetName = 'dropdown_field'; const buttonKey = Key('update_button'); @@ -235,8 +230,10 @@ void main() { DropdownMenuItem(value: 2, child: Text('Option 2')), ]; - final testWidget = - FormBuilderDropdown(name: widgetName, items: initialItems); + final testWidget = FormBuilderDropdown( + name: widgetName, + items: initialItems, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); formKey.currentState?.patchValue({widgetName: 1}); @@ -254,12 +251,13 @@ void main() { DropdownMenuItem(value: 1, child: Text('Option 1')), DropdownMenuItem(value: 2, child: Text('Option 2')), ]; - final testWidget = - FormBuilderDropdown(name: widgetName, items: initialItems); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: initialValue, - )); + final testWidget = FormBuilderDropdown( + name: widgetName, + items: initialItems, + ); + await tester.pumpWidget( + buildTestableFieldWidget(testWidget, initialValue: initialValue), + ); formKey.currentState?.patchValue({widgetName: 2}); await tester.pumpAndSettle(); @@ -268,24 +266,16 @@ void main() { expect(formKey.currentState?.instantValue, equals(initialValue)); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final testWidget = FormBuilderDropdown( name: widgetName, items: const [ - DropdownMenuItem( - value: 1, - child: Text('One'), - ), - DropdownMenuItem( - value: 2, - child: Text('Two'), - ), - DropdownMenuItem( - value: 3, - child: Text('Three'), - ), + DropdownMenuItem(value: 1, child: Text('One')), + DropdownMenuItem(value: 2, child: Text('Two')), + DropdownMenuItem(value: 3, child: Text('Three')), ], ); final widgetFinder = find.byWidget(testWidget); @@ -359,7 +349,7 @@ class _MyTestWidgetState extends State { }); }, child: const Text('update'), - ) + ), ], ); } diff --git a/test/src/fields/form_builder_filter_chips_test.dart b/test/src/fields/form_builder_filter_chips_test.dart index 0d5eff9cc..ab88def2f 100644 --- a/test/src/fields/form_builder_filter_chips_test.dart +++ b/test/src/fields/form_builder_filter_chips_test.dart @@ -43,12 +43,14 @@ void main() { FormBuilderChipOption(key: ValueKey('3'), value: 3), ], ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: { - widgetName: [1] - }, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + initialValue: { + widgetName: [1], + }, + ), + ); await tester.ensureVisible(find.byKey(const ValueKey('1'))); expect(formInstantValue(widgetName), equals([1])); @@ -92,8 +94,9 @@ void main() { expect(formValue?>(widgetName), equals([3])); }); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'key'; final testWidget = FormBuilderFilterChips( name: widgetName, diff --git a/test/src/fields/form_builder_radio_group_test.dart b/test/src/fields/form_builder_radio_group_test.dart index 18701eef1..27d2cdb34 100644 --- a/test/src/fields/form_builder_radio_group_test.dart +++ b/test/src/fields/form_builder_radio_group_test.dart @@ -29,27 +29,29 @@ void main() { expect(formSave(), isTrue); expect(formValue(widgetName), equals(3)); }); - testWidgets('When had a disabled option then can not set this value option', - (WidgetTester tester) async { - const widgetName = 'rg1'; - final testWidget = FormBuilderRadioGroup( - name: widgetName, - disabled: [2], - options: const [ - FormBuilderFieldOption(key: ValueKey('1'), value: 1), - FormBuilderFieldOption(key: ValueKey('2'), value: 2), - FormBuilderFieldOption(key: ValueKey('3'), value: 3), - ], - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + testWidgets( + 'When had a disabled option then can not set this value option', + (WidgetTester tester) async { + const widgetName = 'rg1'; + final testWidget = FormBuilderRadioGroup( + name: widgetName, + disabled: [2], + options: const [ + FormBuilderFieldOption(key: ValueKey('1'), value: 1), + FormBuilderFieldOption(key: ValueKey('2'), value: 2), + FormBuilderFieldOption(key: ValueKey('3'), value: 3), + ], + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - expect(formSave(), isTrue); - expect(formValue(widgetName), isNull); - await tester.tap(find.byKey(const ValueKey('2'))); - await tester.pumpAndSettle(); - expect(formSave(), isTrue); - expect(formValue(widgetName), isNull); - }); + expect(formSave(), isTrue); + expect(formValue(widgetName), isNull); + await tester.tap(find.byKey(const ValueKey('2'))); + await tester.pumpAndSettle(); + expect(formSave(), isTrue); + expect(formValue(widgetName), isNull); + }, + ); testWidgets('decoration horizontal', (WidgetTester tester) async { const widgetName = 'rg1'; @@ -61,8 +63,9 @@ void main() { FormBuilderFieldOption(key: ValueKey('1'), value: 1), FormBuilderFieldOption(key: ValueKey('2'), value: 2), ], - itemDecoration: - BoxDecoration(border: Border.all(color: Colors.blueAccent)), + itemDecoration: BoxDecoration( + border: Border.all(color: Colors.blueAccent), + ), ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -86,8 +89,9 @@ void main() { FormBuilderFieldOption(key: ValueKey('1'), value: 1), FormBuilderFieldOption(key: ValueKey('2'), value: 2), ], - itemDecoration: - BoxDecoration(border: Border.all(color: Colors.blueAccent)), + itemDecoration: BoxDecoration( + border: Border.all(color: Colors.blueAccent), + ), ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -141,8 +145,9 @@ void main() { // verify separator counts expect(find.byType(VerticalDivider), findsNWidgets(2)); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'key'; final testWidget = FormBuilderRadioGroup( name: widgetName, diff --git a/test/src/fields/form_builder_range_slider_test.dart b/test/src/fields/form_builder_range_slider_test.dart index e549e4a63..2f47888ff 100644 --- a/test/src/fields/form_builder_range_slider_test.dart +++ b/test/src/fields/form_builder_range_slider_test.dart @@ -17,15 +17,19 @@ void main() { await tester.pumpWidget(buildTestableFieldWidget(testWidget)); expect(formSave(), isTrue); - expect(formValue(widgetName), - equals(const RangeValues(10.0, 10.0))); + expect( + formValue(widgetName), + equals(const RangeValues(10.0, 10.0)), + ); // Inspired by https://github.com/flutter/flutter/blob/master/packages/flutter/test/material/range_slider_test.dart // Tap at the center of the slider. - final Offset topLeft = - tester.getTopLeft(find.byType(RangeSlider)).translate(24, 0); - final Offset bottomRight = - tester.getBottomRight(find.byType(RangeSlider)).translate(-24, 0); + final Offset topLeft = tester + .getTopLeft(find.byType(RangeSlider)) + .translate(24, 0); + final Offset bottomRight = tester + .getBottomRight(find.byType(RangeSlider)) + .translate(-24, 0); final Offset rightTarget = topLeft + (bottomRight - topLeft) * 0.5; await tester.tapAt(rightTarget); await tester.pumpAndSettle(); @@ -45,15 +49,19 @@ void main() { await tester.pumpWidget(buildTestableFieldWidget(testWidget)); expect(formSave(), isTrue); - expect(formValue(widgetName), - equals(const RangeValues(14.0, 18.0))); + expect( + formValue(widgetName), + equals(const RangeValues(14.0, 18.0)), + ); // Inspired by https://github.com/flutter/flutter/blob/master/packages/flutter/test/material/range_slider_test.dart // Tap a small offset after the start of the slider. - final Offset topLeft = - tester.getTopLeft(find.byType(RangeSlider)).translate(24, 0); - final Offset bottomRight = - tester.getBottomRight(find.byType(RangeSlider)).translate(-24, 0); + final Offset topLeft = tester + .getTopLeft(find.byType(RangeSlider)) + .translate(24, 0); + final Offset bottomRight = tester + .getBottomRight(find.byType(RangeSlider)) + .translate(-24, 0); final Offset leftTarget = topLeft + (bottomRight - topLeft) * 0.1; await tester.tapAt(leftTarget); await tester.pumpAndSettle(); @@ -62,8 +70,9 @@ void main() { expect(formValue(widgetName), const RangeValues(11.0, 18.0)); }); - testWidgets('when set valueWidget then show on FormBuilderRangeSlider', - (WidgetTester tester) async { + testWidgets('when set valueWidget then show on FormBuilderRangeSlider', ( + WidgetTester tester, + ) async { const widgetName = 'formBuilderRangeSlider'; final keyValueWidget = Key('valueWidget'); final testWidget = FormBuilderRangeSlider( @@ -76,8 +85,10 @@ void main() { await tester.pumpWidget(buildTestableFieldWidget(testWidget)); expect(formSave(), isTrue); - expect(formValue(widgetName), - equals(const RangeValues(14.0, 18.0))); + expect( + formValue(widgetName), + equals(const RangeValues(14.0, 18.0)), + ); expect(find.byKey(keyValueWidget), findsOneWidget); }); @@ -102,8 +113,9 @@ void main() { ); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'key'; final testWidget = FormBuilderRangeSlider( name: widgetName, @@ -117,8 +129,10 @@ void main() { formKey.currentState?.fields[widgetName]?.effectiveFocusNode; expect(formSave(), isTrue); - expect(formValue(widgetName), - equals(const RangeValues(10.0, 10.0))); + expect( + formValue(widgetName), + equals(const RangeValues(10.0, 10.0)), + ); expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); expect(focusNode?.hasFocus, false); await tester.sendKeyEvent(LogicalKeyboardKey.tab); @@ -158,7 +172,7 @@ class _FormBuilderRangeSliderStateTestState TextButton( onPressed: () => setState(() => range -= 1), child: const Text('Reduce Range'), - ) + ), ], ); } diff --git a/test/src/fields/form_builder_slider_test.dart b/test/src/fields/form_builder_slider_test.dart index 3f6d8b4d8..6e79f35f5 100644 --- a/test/src/fields/form_builder_slider_test.dart +++ b/test/src/fields/form_builder_slider_test.dart @@ -14,9 +14,7 @@ void main() { min: 0, max: 10, divisions: 20, - decoration: const InputDecoration( - labelText: 'Number of things', - ), + decoration: const InputDecoration(labelText: 'Number of things'), ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -28,8 +26,9 @@ void main() { expect(formValue(widgetName), equals(5.0)); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'key'; final testWidget = FormBuilderSlider( name: widgetName, diff --git a/test/src/fields/form_builder_switch_test.dart b/test/src/fields/form_builder_switch_test.dart index 161e3165b..d71acb0eb 100644 --- a/test/src/fields/form_builder_switch_test.dart +++ b/test/src/fields/form_builder_switch_test.dart @@ -29,8 +29,9 @@ void main() { expect(formValue(widgetName), isFalse); }); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; final testWidget = FormBuilderSwitch( name: widgetName, diff --git a/test/src/fields/form_builder_text_field_test.dart b/test/src/fields/form_builder_text_field_test.dart index 7aedc8500..d60ab7191 100644 --- a/test/src/fields/form_builder_text_field_test.dart +++ b/test/src/fields/form_builder_text_field_test.dart @@ -10,9 +10,7 @@ void main() { testWidgets('Hello Planet', (WidgetTester tester) async { const newTextValue = 'Hello 🪐'; const textFieldName = 'text1'; - final testWidget = FormBuilderTextField( - name: textFieldName, - ); + final testWidget = FormBuilderTextField(name: textFieldName); final widgetFinder = find.byWidget(testWidget); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -27,9 +25,7 @@ void main() { }); testWidgets( 'without initialValue', - (tester) => _testFormBuilderTextFieldWithInitialValue( - tester, - ), + (tester) => _testFormBuilderTextFieldWithInitialValue(tester), ); testWidgets( 'has initialValue on Field', @@ -50,12 +46,11 @@ void main() { 'triggers onTapOutside', (tester) => _testFormBuilderTextFieldOnTapOutsideCallback(tester), ); - testWidgets('When press tab, field will be focused', - (WidgetTester tester) async { + testWidgets('When press tab, field will be focused', ( + WidgetTester tester, + ) async { const widgetName = 'cb1'; - var testWidget = FormBuilderTextField( - name: widgetName, - ); + var testWidget = FormBuilderTextField(name: widgetName); final widgetFinder = find.byWidget(testWidget); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -88,12 +83,12 @@ Future _testFormBuilderTextFieldWithInitialValue( initialValue: initialValueOnField, onChanged: (String? value) => changedCount++, ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: { - textFieldName: initialValueOnForm, - }, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + initialValue: {textFieldName: initialValueOnForm}, + ), + ); expect(formSave(), isTrue); expect(formValue(textFieldName), initialValueOnField ?? initialValueOnForm); expect(changedCount, 0); @@ -106,7 +101,8 @@ Future _testFormBuilderTextFieldWithInitialValue( } Future _testFormBuilderTextFieldOnTapOutsideCallback( - WidgetTester tester) async { + WidgetTester tester, +) async { const textFieldName = 'Hello 🪐'; bool triggered = false; @@ -114,9 +110,7 @@ Future _testFormBuilderTextFieldOnTapOutsideCallback( name: textFieldName, onTapOutside: (event) => triggered = true, ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - )); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); final textField = tester.firstWidget(find.byType(TextField)) as TextField; textField.onTapOutside?.call(const PointerDownEvent()); expect(triggered, true); diff --git a/test/src/form_builder_field_decoration_test.dart b/test/src/form_builder_field_decoration_test.dart index 744248cd9..96a848459 100644 --- a/test/src/form_builder_field_decoration_test.dart +++ b/test/src/form_builder_field_decoration_test.dart @@ -6,8 +6,9 @@ import '../form_builder_tester.dart'; void main() { group('FormBuilderFieldDecoration -', () { - testWidgets('when change the error text then the field should be invalid', - (WidgetTester tester) async { + testWidgets('when change the error text then the field should be invalid', ( + WidgetTester tester, + ) async { final decorationFieldKey = GlobalKey(); const name = 'testField'; const errorTextField = 'error text field'; @@ -43,53 +44,16 @@ void main() { expect(decorationFieldKey.currentState?.isValid, isTrue); }); group('decoration enabled -', () { - testWidgets('when change the error text then the field should be invalid', - (WidgetTester tester) async { - final decorationFieldKey = GlobalKey(); - const name = 'testField'; - const errorTextField = 'error text field'; - final widget = FormBuilderFieldDecoration( - key: decorationFieldKey, - name: name, - builder: (FormFieldState field) { - return InputDecorator( - decoration: (field as FormBuilderFieldDecorationState).decoration, - child: TextField( - onChanged: (value) { - field.didChange(value); - }, - ), - ); - }, - ); - - await tester.pumpWidget(buildTestableFieldWidget(widget)); - - // Initially, the field should be valid - expect(decorationFieldKey.currentState?.isValid, isTrue); - - decorationFieldKey.currentState?.invalidate(errorTextField); - - // The field should be invalid - expect(decorationFieldKey.currentState?.isValid, isFalse); - - // Clear the error - decorationFieldKey.currentState?.reset(); - - // The field should be valid again - expect(decorationFieldKey.currentState?.isValid, isTrue); - }); - test( - 'when enable property on decoration is false and enabled true, then throw an assert', - () async { - final decorationFieldKey = GlobalKey(); - const name = 'testField'; - - expect( - () => FormBuilderFieldDecoration( + testWidgets( + 'when change the error text then the field should be invalid', + (WidgetTester tester) async { + final decorationFieldKey = + GlobalKey(); + const name = 'testField'; + const errorTextField = 'error text field'; + final widget = FormBuilderFieldDecoration( key: decorationFieldKey, name: name, - decoration: const InputDecoration(enabled: false), builder: (FormFieldState field) { return InputDecorator( decoration: @@ -101,87 +65,137 @@ void main() { ), ); }, - ), - throwsAssertionError, - ); - }); - test( - 'when enable property on decoration is false and enable is false, then build normally', - () async { - final decorationFieldKey = GlobalKey(); - const name = 'testField'; + ); - expect( - () => FormBuilderFieldDecoration( - key: decorationFieldKey, - name: name, - decoration: const InputDecoration(enabled: false), - enabled: false, - builder: (FormFieldState field) { - return InputDecorator( - decoration: - (field as FormBuilderFieldDecorationState).decoration, - child: TextField( - onChanged: (value) { - field.didChange(value); - }, - ), - ); - }, - ), - returnsNormally, - ); - }); - test('when decoration is default (enabled: true), then build normally', - () async { - final decorationFieldKey = GlobalKey(); - const name = 'testField'; - - expect( - () => FormBuilderFieldDecoration( - key: decorationFieldKey, - name: name, - builder: (FormFieldState field) { - return InputDecorator( - decoration: - (field as FormBuilderFieldDecorationState).decoration, - child: TextField( - onChanged: (value) { - field.didChange(value); - }, - ), - ); - }, - ), - returnsNormally, - ); - }); - test( - 'when decoration is default (enabled: true) and enable false, then build normally', - () async { - final decorationFieldKey = GlobalKey(); - const name = 'testField'; + await tester.pumpWidget(buildTestableFieldWidget(widget)); - expect( - () => FormBuilderFieldDecoration( - key: decorationFieldKey, - name: name, - enabled: false, - builder: (FormFieldState field) { - return InputDecorator( - decoration: - (field as FormBuilderFieldDecorationState).decoration, - child: TextField( - onChanged: (value) { - field.didChange(value); - }, - ), - ); - }, - ), - returnsNormally, - ); - }); + // Initially, the field should be valid + expect(decorationFieldKey.currentState?.isValid, isTrue); + + decorationFieldKey.currentState?.invalidate(errorTextField); + + // The field should be invalid + expect(decorationFieldKey.currentState?.isValid, isFalse); + + // Clear the error + decorationFieldKey.currentState?.reset(); + + // The field should be valid again + expect(decorationFieldKey.currentState?.isValid, isTrue); + }, + ); + test( + 'when enable property on decoration is false and enabled true, then throw an assert', + () async { + final decorationFieldKey = + GlobalKey(); + const name = 'testField'; + + expect( + () => FormBuilderFieldDecoration( + key: decorationFieldKey, + name: name, + decoration: const InputDecoration(enabled: false), + builder: (FormFieldState field) { + return InputDecorator( + decoration: + (field as FormBuilderFieldDecorationState).decoration, + child: TextField( + onChanged: (value) { + field.didChange(value); + }, + ), + ); + }, + ), + throwsAssertionError, + ); + }, + ); + test( + 'when enable property on decoration is false and enable is false, then build normally', + () async { + final decorationFieldKey = + GlobalKey(); + const name = 'testField'; + + expect( + () => FormBuilderFieldDecoration( + key: decorationFieldKey, + name: name, + decoration: const InputDecoration(enabled: false), + enabled: false, + builder: (FormFieldState field) { + return InputDecorator( + decoration: + (field as FormBuilderFieldDecorationState).decoration, + child: TextField( + onChanged: (value) { + field.didChange(value); + }, + ), + ); + }, + ), + returnsNormally, + ); + }, + ); + test( + 'when decoration is default (enabled: true), then build normally', + () async { + final decorationFieldKey = + GlobalKey(); + const name = 'testField'; + + expect( + () => FormBuilderFieldDecoration( + key: decorationFieldKey, + name: name, + builder: (FormFieldState field) { + return InputDecorator( + decoration: + (field as FormBuilderFieldDecorationState).decoration, + child: TextField( + onChanged: (value) { + field.didChange(value); + }, + ), + ); + }, + ), + returnsNormally, + ); + }, + ); + test( + 'when decoration is default (enabled: true) and enable false, then build normally', + () async { + final decorationFieldKey = + GlobalKey(); + const name = 'testField'; + + expect( + () => FormBuilderFieldDecoration( + key: decorationFieldKey, + name: name, + enabled: false, + builder: (FormFieldState field) { + return InputDecorator( + decoration: + (field as FormBuilderFieldDecorationState).decoration, + child: TextField( + onChanged: (value) { + field.didChange(value); + }, + ), + ); + }, + ), + returnsNormally, + ); + }, + ); }); }); } diff --git a/test/src/form_builder_field_test.dart b/test/src/form_builder_field_test.dart index 356292265..122fcc630 100644 --- a/test/src/form_builder_field_test.dart +++ b/test/src/form_builder_field_test.dart @@ -8,8 +8,9 @@ import '../form_builder_tester.dart'; void main() { group('FormBuilderField -', () { group('custom error -', () { - testWidgets('Should show custom error when invalidate field', - (tester) async { + testWidgets('Should show custom error when invalidate field', ( + tester, + ) async { final textFieldKey = GlobalKey(); const textFieldName = 'text2'; const errorTextField = 'error text field'; @@ -44,51 +45,55 @@ void main() { expect(textFieldKey.currentState?.isValid, isFalse); }); testWidgets( - 'Should valid when no has error and autovalidateMode is always', - (tester) async { - final textFieldKey = GlobalKey(); - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - key: textFieldKey, - autovalidateMode: AutovalidateMode.always, - validator: (value) => - value == null || value.isEmpty ? errorTextField : null, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - - expect(textFieldKey.currentState?.isValid, isFalse); - - final widgetFinder = find.byWidget(testWidget); - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); - - expect(textFieldKey.currentState?.isValid, isTrue); - }); + 'Should valid when no has error and autovalidateMode is always', + (tester) async { + final textFieldKey = GlobalKey(); + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + autovalidateMode: AutovalidateMode.always, + validator: + (value) => + value == null || value.isEmpty ? errorTextField : null, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + expect(textFieldKey.currentState?.isValid, isFalse); + + final widgetFinder = find.byWidget(testWidget); + await tester.enterText(widgetFinder, 'test'); + await tester.pumpAndSettle(); + + expect(textFieldKey.currentState?.isValid, isTrue); + }, + ); testWidgets( - 'Should invalid when has error and autovalidateMode is always', - (tester) async { - final textFieldKey = GlobalKey(); - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - key: textFieldKey, - autovalidateMode: AutovalidateMode.always, - validator: (value) => - value == null || value.length < 10 ? errorTextField : null, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - - expect(textFieldKey.currentState?.isValid, isFalse); - - final widgetFinder = find.byWidget(testWidget); - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); - - expect(textFieldKey.currentState?.isValid, isFalse); - }); + 'Should invalid when has error and autovalidateMode is always', + (tester) async { + final textFieldKey = GlobalKey(); + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + autovalidateMode: AutovalidateMode.always, + validator: + (value) => + value == null || value.length < 10 ? errorTextField : null, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + expect(textFieldKey.currentState?.isValid, isFalse); + + final widgetFinder = find.byWidget(testWidget); + await tester.enterText(widgetFinder, 'test'); + await tester.pumpAndSettle(); + + expect(textFieldKey.currentState?.isValid, isFalse); + }, + ); }); group('hasErrors -', () { @@ -108,8 +113,9 @@ void main() { expect(textFieldKey.currentState?.hasError, isTrue); }); - testWidgets('Should no has errors when is empty and no has validators', - (tester) async { + testWidgets('Should no has errors when is empty and no has validators', ( + tester, + ) async { final textFieldKey = GlobalKey(); const textFieldName = 'text'; final testWidget = FormBuilderTextField( @@ -128,28 +134,30 @@ void main() { group('valueIsValid -', () { testWidgets( - 'Should value is valid when validator passes, despite set custom error', - (tester) async { - final textFieldKey = GlobalKey(); - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - key: textFieldKey, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - - // Set custom error - textFieldKey.currentState?.invalidate(errorTextField); - await tester.pumpAndSettle(); - - expect(textFieldKey.currentState?.valueIsValid, isTrue); - }); + 'Should value is valid when validator passes, despite set custom error', + (tester) async { + final textFieldKey = GlobalKey(); + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + // Set custom error + textFieldKey.currentState?.invalidate(errorTextField); + await tester.pumpAndSettle(); + + expect(textFieldKey.currentState?.valueIsValid, isTrue); + }, + ); }); group('valueHasError -', () { - testWidgets('Should value is invalid when validator passes', - (tester) async { + testWidgets('Should value is invalid when validator passes', ( + tester, + ) async { final textFieldKey = GlobalKey(); const textFieldName = 'text'; const invalidValue = 'invalid'; @@ -167,112 +175,124 @@ void main() { }); group('autovalidateMode -', () { testWidgets( - 'Should show error when init form and AutovalidateMode is always', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => - value == null || value.isEmpty ? errorTextField : null, - autovalidateMode: AutovalidateMode.always, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - await tester.pumpAndSettle(); - - expect(find.text(errorTextField), findsOneWidget); - }); + 'Should show error when init form and AutovalidateMode is always', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: + (value) => + value == null || value.isEmpty ? errorTextField : null, + autovalidateMode: AutovalidateMode.always, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + await tester.pumpAndSettle(); + + expect(find.text(errorTextField), findsOneWidget); + }, + ); testWidgets( - 'Should not show error when init form and AutovalidateMode is disabled', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - autovalidateMode: AutovalidateMode.disabled, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - await tester.pumpAndSettle(); - - expect(find.text(errorTextField), findsNothing); - }); + 'Should not show error when init form and AutovalidateMode is disabled', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + autovalidateMode: AutovalidateMode.disabled, + validator: (value) => errorTextField, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + await tester.pumpAndSettle(); + + expect(find.text(errorTextField), findsNothing); + }, + ); testWidgets( - 'Should show error when AutovalidateMode is onUserInteraction and change field', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - expect(find.text(errorTextField), findsNothing); - - await tester.enterText(find.byWidget(testWidget), 'hola'); - await tester.pumpAndSettle(); - - expect(find.text(errorTextField), findsOneWidget); - }); + 'Should show error when AutovalidateMode is onUserInteraction and change field', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (value) => errorTextField, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + expect(find.text(errorTextField), findsNothing); + + await tester.enterText(find.byWidget(testWidget), 'hola'); + await tester.pumpAndSettle(); + + expect(find.text(errorTextField), findsOneWidget); + }, + ); testWidgets( - 'Should show error when init form and AutovalidateMode is onUnfocus', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - autovalidateMode: AutovalidateMode.onUnfocus, - validator: (value) => errorTextField, - ); - final widgetFinder = find.byWidget(testWidget); - - // Init form - await tester.pumpWidget(buildTestableFieldWidget( - Column( - children: [ - testWidget, - ElevatedButton(onPressed: () {}, child: const Text('Submit')), - ], - ), - autovalidateMode: AutovalidateMode.onUnfocus, - )); - await tester.pumpAndSettle(); - final focusNode = - formKey.currentState?.fields[textFieldName]?.effectiveFocusNode; - expect(find.text(errorTextField), findsNothing); - expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); - expect(focusNode?.hasFocus, false); - - // Focus input and write text - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); - expect(Focus.of(tester.element(widgetFinder)).hasFocus, true); - expect(focusNode?.hasFocus, true); - expect(find.text(errorTextField), findsNothing); - - // Unfocus input and show error - await tester.sendKeyEvent(LogicalKeyboardKey.tab); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsOneWidget); - }); + 'Should show error when init form and AutovalidateMode is onUnfocus', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + autovalidateMode: AutovalidateMode.onUnfocus, + validator: (value) => errorTextField, + ); + final widgetFinder = find.byWidget(testWidget); + + // Init form + await tester.pumpWidget( + buildTestableFieldWidget( + Column( + children: [ + testWidget, + ElevatedButton(onPressed: () {}, child: const Text('Submit')), + ], + ), + autovalidateMode: AutovalidateMode.onUnfocus, + ), + ); + await tester.pumpAndSettle(); + final focusNode = + formKey.currentState?.fields[textFieldName]?.effectiveFocusNode; + expect(find.text(errorTextField), findsNothing); + expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); + expect(focusNode?.hasFocus, false); + + // Focus input and write text + await tester.enterText(widgetFinder, 'test'); + await tester.pumpAndSettle(); + expect(Focus.of(tester.element(widgetFinder)).hasFocus, true); + expect(focusNode?.hasFocus, true); + expect(find.text(errorTextField), findsNothing); + + // Unfocus input and show error + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsOneWidget); + }, + ); }); group('isDirty - ', () { testWidgets('Should not dirty by default', (tester) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); expect(textFieldKey.currentState?.isDirty, false); }); - testWidgets('Should dirty when update field value by user', - (tester) async { + testWidgets('Should dirty when update field value by user', ( + tester, + ) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); final widgetFinder = find.byWidget(testWidget); @@ -280,12 +300,15 @@ void main() { expect(textFieldKey.currentState?.isDirty, true); }); - testWidgets('Should dirty when update field value by method', - (tester) async { + testWidgets('Should dirty when update field value by method', ( + tester, + ) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); textFieldKey.currentState?.setValue('test'); @@ -293,8 +316,9 @@ void main() { expect(textFieldKey.currentState?.isDirty, true); }); - testWidgets('Should dirty when update field with initial value by user', - (tester) async { + testWidgets('Should dirty when update field with initial value by user', ( + tester, + ) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); final testWidget = FormBuilderTextField( @@ -309,37 +333,42 @@ void main() { expect(textFieldKey.currentState?.isDirty, true); }); - testWidgets('Should dirty when update field with initial value by method', - (tester) async { + testWidgets( + 'Should dirty when update field with initial value by method', + (tester) async { + const textFieldName = 'text'; + final textFieldKey = GlobalKey(); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + initialValue: 'hi', + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + textFieldKey.currentState?.setValue('test'); + await tester.pumpAndSettle(); + + expect(textFieldKey.currentState?.isDirty, true); + }, + ); + testWidgets('Should not dirty when reset field value', (tester) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); final testWidget = FormBuilderTextField( name: textFieldName, key: textFieldKey, - initialValue: 'hi', ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - textFieldKey.currentState?.setValue('test'); - await tester.pumpAndSettle(); - - expect(textFieldKey.currentState?.isDirty, true); - }); - testWidgets('Should not dirty when reset field value', (tester) async { - const textFieldName = 'text'; - final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - textFieldKey.currentState?.setValue('test'); await tester.pumpAndSettle(); textFieldKey.currentState?.reset(); expect(textFieldKey.currentState?.isDirty, false); }); - testWidgets('Should not dirty when reset field with initial value', - (tester) async { + testWidgets('Should not dirty when reset field with initial value', ( + tester, + ) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); final testWidget = FormBuilderTextField( @@ -360,8 +389,10 @@ void main() { testWidgets('Should not touched by default', (tester) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); expect(textFieldKey.currentState?.isTouched, false); @@ -369,8 +400,10 @@ void main() { testWidgets('Should touched when focus input', (tester) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); final widgetFinder = find.byWidget(testWidget); @@ -383,8 +416,10 @@ void main() { testWidgets('Should reset to null when call reset', (tester) async { const textFieldName = 'text'; final textFieldKey = GlobalKey(); - final testWidget = - FormBuilderTextField(name: textFieldName, key: textFieldKey); + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); textFieldKey.currentState?.setValue('test'); @@ -411,25 +446,26 @@ void main() { expect(textFieldKey.currentState?.value, equals(initialValue)); }); testWidgets( - 'Should reset custom error when invalidate field and then reset', - (tester) async { - final textFieldKey = GlobalKey(); - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - key: textFieldKey, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - - textFieldKey.currentState?.invalidate(errorTextField); - await tester.pumpAndSettle(); - - // Reset custom error - textFieldKey.currentState?.reset(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); - }); + 'Should reset custom error when invalidate field and then reset', + (tester) async { + final textFieldKey = GlobalKey(); + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + key: textFieldKey, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + + textFieldKey.currentState?.invalidate(errorTextField); + await tester.pumpAndSettle(); + + // Reset custom error + textFieldKey.currentState?.reset(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsNothing); + }, + ); }); group('focus -', () { testWidgets('Should focus on field when invalidate it', (tester) async { @@ -456,31 +492,32 @@ void main() { expect(focusNode?.hasFocus, true); }); testWidgets( - 'Should not focus on field when invalidate field and is disabled', - (tester) async { - final textFieldKey = GlobalKey(); - const widgetName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: widgetName, - key: textFieldKey, - enabled: false, - ); - final widgetFinder = find.byWidget(testWidget); - - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - final focusNode = - formKey.currentState?.fields[widgetName]?.effectiveFocusNode; - - expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); - expect(focusNode?.hasFocus, false); - - textFieldKey.currentState?.invalidate(errorTextField); - await tester.pumpAndSettle(); - - expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); - expect(focusNode?.hasFocus, false); - }); + 'Should not focus on field when invalidate field and is disabled', + (tester) async { + final textFieldKey = GlobalKey(); + const widgetName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: widgetName, + key: textFieldKey, + enabled: false, + ); + final widgetFinder = find.byWidget(testWidget); + + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + final focusNode = + formKey.currentState?.fields[widgetName]?.effectiveFocusNode; + + expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); + expect(focusNode?.hasFocus, false); + + textFieldKey.currentState?.invalidate(errorTextField); + await tester.pumpAndSettle(); + + expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); + expect(focusNode?.hasFocus, false); + }, + ); }); }); } diff --git a/test/src/form_builder_test.dart b/test/src/form_builder_test.dart index 17028277f..ff4c212cd 100644 --- a/test/src/form_builder_test.dart +++ b/test/src/form_builder_test.dart @@ -7,8 +7,9 @@ import '../form_builder_tester.dart'; void main() { group('custom error -', () { - testWidgets('Should show custom error when invalidate field', - (tester) async { + testWidgets('Should show custom error when invalidate field', ( + tester, + ) async { const textFieldName = 'text1'; const errorTextField = 'error text field'; final testWidget = FormBuilderTextField(name: textFieldName); @@ -22,105 +23,105 @@ void main() { }); group('dynamic fields', () { - testWidgets( - 'FormBuilder Dynamic Field -- keeping value', - (WidgetTester tester) async { - const String testWidgetName = 'test_widget_name'; - - await tester.pumpWidget( - buildTestableFieldWidget( - _DynamicFormFields( - name: testWidgetName, - valueTransformer: (value) { - return value is String ? int.tryParse(value) : null; - }, - ), - // the value is kept - clearValueOnUnregister: false, + testWidgets('FormBuilder Dynamic Field -- keeping value', ( + WidgetTester tester, + ) async { + const String testWidgetName = 'test_widget_name'; + + await tester.pumpWidget( + buildTestableFieldWidget( + _DynamicFormFields( + name: testWidgetName, + valueTransformer: (value) { + return value is String ? int.tryParse(value) : null; + }, ), - ); - - // Write an input resulting in a null value after transformation - formFieldDidChange(testWidgetName, 'a'); - expect(formInstantValue(testWidgetName), isNull); + // the value is kept + clearValueOnUnregister: false, + ), + ); - // Write an input and test the transformer - formFieldDidChange(testWidgetName, '1'); - expect(formInstantValue(testWidgetName).runtimeType, int); - expect(formInstantValue(testWidgetName), 1); + // Write an input resulting in a null value after transformation + formFieldDidChange(testWidgetName, 'a'); + expect(formInstantValue(testWidgetName), isNull); - // Remove the dynamic field from the widget tree - final _DynamicFormFieldsState dynamicFieldState = - tester.state(find.byType(_DynamicFormFields)); - dynamicFieldState.show = false; + // Write an input and test the transformer + formFieldDidChange(testWidgetName, '1'); + expect(formInstantValue(testWidgetName).runtimeType, int); + expect(formInstantValue(testWidgetName), 1); - // Pump the next frame, disposing the field's state - await tester.pump(); + // Remove the dynamic field from the widget tree + final _DynamicFormFieldsState dynamicFieldState = tester.state( + find.byType(_DynamicFormFields), + ); + dynamicFieldState.show = false; - // With the field unregistered, the form does not have its transformer - // but it still has its value, now recovered as type String - expect(formInstantValue(testWidgetName).runtimeType, String); - expect(formInstantValue(testWidgetName), '1'); + // Pump the next frame, disposing the field's state + await tester.pump(); - // Show and recreate the field's state - dynamicFieldState.show = true; - await tester.pump(); + // With the field unregistered, the form does not have its transformer + // but it still has its value, now recovered as type String + expect(formInstantValue(testWidgetName).runtimeType, String); + expect(formInstantValue(testWidgetName), '1'); - // The transformer is registered again and with the internal value that - // was kept, it's expected an int of value 1 - expect(formInstantValue(testWidgetName).runtimeType, int); - expect(formInstantValue(testWidgetName), 1); - }, - ); + // Show and recreate the field's state + dynamicFieldState.show = true; + await tester.pump(); - testWidgets( - 'FormBuilder Dynamic Field -- clearing value', - (WidgetTester tester) async { - const String testWidgetName = 'test_widget_name'; + // The transformer is registered again and with the internal value that + // was kept, it's expected an int of value 1 + expect(formInstantValue(testWidgetName).runtimeType, int); + expect(formInstantValue(testWidgetName), 1); + }); - await tester.pumpWidget( - buildTestableFieldWidget( - _DynamicFormFields( - name: testWidgetName, - valueTransformer: (value) { - return value is String ? int.tryParse(value) : null; - }, - ), - // the value is cleared - clearValueOnUnregister: true, + testWidgets('FormBuilder Dynamic Field -- clearing value', ( + WidgetTester tester, + ) async { + const String testWidgetName = 'test_widget_name'; + + await tester.pumpWidget( + buildTestableFieldWidget( + _DynamicFormFields( + name: testWidgetName, + valueTransformer: (value) { + return value is String ? int.tryParse(value) : null; + }, ), - ); + // the value is cleared + clearValueOnUnregister: true, + ), + ); - // Write an input and test the transformer - formFieldDidChange(testWidgetName, '1'); - await tester.pump(); - expect(int, formInstantValue(testWidgetName).runtimeType); - expect(1, formInstantValue(testWidgetName)); - - // Remove the dynamic field from the widget tree - final _DynamicFormFieldsState dynamicFieldState = - tester.state(find.byType(_DynamicFormFields)); - dynamicFieldState.show = false; - - // Pump the next frame, disposing the field's state - await tester.pump(); - - // With the field unregistered, the form does not have its transformer, - // and since the value was cleared, neither its value - expect(formInstantValue(testWidgetName).runtimeType, Null); - expect(formInstantValue(testWidgetName), isNull); - - // Show and recreate the field's state - dynamicFieldState.show = true; - await tester.pump(); - - // A new input is needed to get another value - formFieldDidChange(testWidgetName, '2'); - await tester.pump(); - expect(formInstantValue(testWidgetName).runtimeType, int); - expect(formInstantValue(testWidgetName), 2); - }, - ); + // Write an input and test the transformer + formFieldDidChange(testWidgetName, '1'); + await tester.pump(); + expect(int, formInstantValue(testWidgetName).runtimeType); + expect(1, formInstantValue(testWidgetName)); + + // Remove the dynamic field from the widget tree + final _DynamicFormFieldsState dynamicFieldState = tester.state( + find.byType(_DynamicFormFields), + ); + dynamicFieldState.show = false; + + // Pump the next frame, disposing the field's state + await tester.pump(); + + // With the field unregistered, the form does not have its transformer, + // and since the value was cleared, neither its value + expect(formInstantValue(testWidgetName).runtimeType, Null); + expect(formInstantValue(testWidgetName), isNull); + + // Show and recreate the field's state + dynamicFieldState.show = true; + await tester.pump(); + + // A new input is needed to get another value + formFieldDidChange(testWidgetName, '2'); + await tester.pump(); + expect(formInstantValue(testWidgetName).runtimeType, int); + expect(formInstantValue(testWidgetName), 2); + }); }); group('isValid -', () { @@ -136,219 +137,244 @@ void main() { expect(formKey.currentState?.isValid, isFalse); }); - testWidgets('Should valid when no has error and autovalidateMode is always', - (tester) async { - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => - value == null || value.isEmpty ? errorTextField : null, - ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - autovalidateMode: AutovalidateMode.always, - )); + testWidgets( + 'Should valid when no has error and autovalidateMode is always', + (tester) async { + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: + (value) => value == null || value.isEmpty ? errorTextField : null, + ); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + autovalidateMode: AutovalidateMode.always, + ), + ); - expect(formKey.currentState?.isValid, isFalse); + expect(formKey.currentState?.isValid, isFalse); - final widgetFinder = find.byWidget(testWidget); - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); + final widgetFinder = find.byWidget(testWidget); + await tester.enterText(widgetFinder, 'test'); + await tester.pumpAndSettle(); - expect(formKey.currentState?.isValid, isTrue); - }); - testWidgets('Should invalid when has error and autovalidateMode is always', - (tester) async { - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => - value == null || value.length < 10 ? errorTextField : null, - ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - autovalidateMode: AutovalidateMode.always, - )); + expect(formKey.currentState?.isValid, isTrue); + }, + ); + testWidgets( + 'Should invalid when has error and autovalidateMode is always', + (tester) async { + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: + (value) => + value == null || value.length < 10 ? errorTextField : null, + ); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + autovalidateMode: AutovalidateMode.always, + ), + ); - expect(formKey.currentState?.isValid, isFalse); + expect(formKey.currentState?.isValid, isFalse); - final widgetFinder = find.byWidget(testWidget); - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); + final widgetFinder = find.byWidget(testWidget); + await tester.enterText(widgetFinder, 'test'); + await tester.pumpAndSettle(); - expect(formKey.currentState?.isValid, isFalse); - }); + expect(formKey.currentState?.isValid, isFalse); + }, + ); }); group('skipDisabled -', () { testWidgets( - 'Should not show error when field is disabled and skipDisabled is true', - (tester) async { - const textFieldName = 'text3'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - enabled: false, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - skipDisabled: true, - )); + 'Should not show error when field is disabled and skipDisabled is true', + (tester) async { + const textFieldName = 'text3'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + enabled: false, + validator: (value) => errorTextField, + ); + await tester.pumpWidget( + buildTestableFieldWidget(testWidget, skipDisabled: true), + ); - formKey.currentState?.validate(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); + formKey.currentState?.validate(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsNothing); - formKey.currentState?.fields[textFieldName]?.validate(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); - }); + formKey.currentState?.fields[textFieldName]?.validate(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsNothing); + }, + ); testWidgets( - 'Should show error when field is disabled and skipDisabled is false', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - enabled: false, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + 'Should show error when field is disabled and skipDisabled is false', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + enabled: false, + validator: (value) => errorTextField, + ); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - formKey.currentState?.validate(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsOneWidget); + formKey.currentState?.validate(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsOneWidget); - formKey.currentState?.reset(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); + formKey.currentState?.reset(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsNothing); - formKey.currentState?.fields[textFieldName]?.validate(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsOneWidget); - }); + formKey.currentState?.fields[textFieldName]?.validate(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsOneWidget); + }, + ); }); group('autovalidateMode -', () { testWidgets( - 'Should show error when init form and AutovalidateMode is always', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - autovalidateMode: AutovalidateMode.always, - )); - await tester.pumpAndSettle(); + 'Should show error when init form and AutovalidateMode is always', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: (value) => errorTextField, + ); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + autovalidateMode: AutovalidateMode.always, + ), + ); + await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsOneWidget); - }); + expect(find.text(errorTextField), findsOneWidget); + }, + ); testWidgets( - 'Should not show error when init form and AutovalidateMode is disabled', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - autovalidateMode: AutovalidateMode.disabled, - )); - await tester.pumpAndSettle(); + 'Should not show error when init form and AutovalidateMode is disabled', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: (value) => errorTextField, + ); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + autovalidateMode: AutovalidateMode.disabled, + ), + ); + await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); - }); + expect(find.text(errorTextField), findsNothing); + }, + ); testWidgets( - 'Should show error when init form and AutovalidateMode is onUserInteraction', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => errorTextField, - ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - autovalidateMode: AutovalidateMode.onUserInteraction, - )); - await tester.pumpAndSettle(); + 'Should show error when init form and AutovalidateMode is onUserInteraction', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: (value) => errorTextField, + ); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + autovalidateMode: AutovalidateMode.onUserInteraction, + ), + ); + await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); + expect(find.text(errorTextField), findsNothing); - final widgetFinder = find.byWidget(testWidget); - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); + final widgetFinder = find.byWidget(testWidget); + await tester.enterText(widgetFinder, 'test'); + await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsOneWidget); - }); + expect(find.text(errorTextField), findsOneWidget); + }, + ); testWidgets( - 'Should show error when init form and AutovalidateMode is onUnfocus', - (tester) async { - const textFieldName = 'text4'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField( - name: textFieldName, - validator: (value) => errorTextField, - ); - final widgetFinder = find.byWidget(testWidget); - - // Init form - await tester.pumpWidget(buildTestableFieldWidget( - Column( - children: [ - testWidget, - ElevatedButton(onPressed: () {}, child: const Text('Submit')), - ], - ), - autovalidateMode: AutovalidateMode.onUnfocus, - )); - await tester.pumpAndSettle(); - final focusNode = - formKey.currentState?.fields[textFieldName]?.effectiveFocusNode; - expect(find.text(errorTextField), findsNothing); - expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); - expect(focusNode?.hasFocus, false); - - // Focus input and write text - await tester.enterText(widgetFinder, 'test'); - await tester.pumpAndSettle(); - expect(Focus.of(tester.element(widgetFinder)).hasFocus, true); - expect(focusNode?.hasFocus, true); - expect(find.text(errorTextField), findsNothing); - - // Unfocus input and show error - await tester.sendKeyEvent(LogicalKeyboardKey.tab); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsOneWidget); - }); - group('Interact with FormBuilderField -', () { - testWidgets( - 'Should show error when FormBuilder is disabled and FormBuilderField is always', - (tester) async { + 'Should show error when init form and AutovalidateMode is onUnfocus', + (tester) async { const textFieldName = 'text4'; const errorTextField = 'error text field'; final testWidget = FormBuilderTextField( name: textFieldName, validator: (value) => errorTextField, - autovalidateMode: AutovalidateMode.always, ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - autovalidateMode: AutovalidateMode.disabled, - )); + final widgetFinder = find.byWidget(testWidget); + + // Init form + await tester.pumpWidget( + buildTestableFieldWidget( + Column( + children: [ + testWidget, + ElevatedButton(onPressed: () {}, child: const Text('Submit')), + ], + ), + autovalidateMode: AutovalidateMode.onUnfocus, + ), + ); + await tester.pumpAndSettle(); + final focusNode = + formKey.currentState?.fields[textFieldName]?.effectiveFocusNode; + expect(find.text(errorTextField), findsNothing); + expect(Focus.of(tester.element(widgetFinder)).hasFocus, false); + expect(focusNode?.hasFocus, false); + + // Focus input and write text + await tester.enterText(widgetFinder, 'test'); await tester.pumpAndSettle(); + expect(Focus.of(tester.element(widgetFinder)).hasFocus, true); + expect(focusNode?.hasFocus, true); + expect(find.text(errorTextField), findsNothing); + // Unfocus input and show error + await tester.sendKeyEvent(LogicalKeyboardKey.tab); + await tester.pumpAndSettle(); expect(find.text(errorTextField), findsOneWidget); - }); + }, + ); + group('Interact with FormBuilderField -', () { + testWidgets( + 'Should show error when FormBuilder is disabled and FormBuilderField is always', + (tester) async { + const textFieldName = 'text4'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField( + name: textFieldName, + validator: (value) => errorTextField, + autovalidateMode: AutovalidateMode.always, + ); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + autovalidateMode: AutovalidateMode.disabled, + ), + ); + await tester.pumpAndSettle(); + + expect(find.text(errorTextField), findsOneWidget); + }, + ); // TODO: Enable when solve issue // https://github.com/flutter/flutter/issues/125766 // testWidgets( @@ -390,8 +416,9 @@ void main() { expect(formKey.currentState?.isDirty, true); }); - testWidgets('Should dirty when update field value by method', - (tester) async { + testWidgets('Should dirty when update field value by method', ( + tester, + ) async { const textFieldName = 'text'; final testWidget = FormBuilderTextField(name: textFieldName); await tester.pumpWidget(buildTestableFieldWidget(testWidget)); @@ -401,28 +428,34 @@ void main() { expect(formKey.currentState?.isDirty, true); }); - testWidgets('Should dirty when update field with initial value by user', - (tester) async { + testWidgets('Should dirty when update field with initial value by user', ( + tester, + ) async { const textFieldName = 'text'; final testWidget = FormBuilderTextField(name: textFieldName); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: {textFieldName: 'hi'}, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + initialValue: {textFieldName: 'hi'}, + ), + ); final widgetFinder = find.byWidget(testWidget); await tester.enterText(widgetFinder, 'test'); expect(formKey.currentState?.isDirty, true); }); - testWidgets('Should dirty when update field with initial value by method', - (tester) async { + testWidgets('Should dirty when update field with initial value by method', ( + tester, + ) async { const textFieldName = 'text'; final testWidget = FormBuilderTextField(name: textFieldName); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: {textFieldName: 'hi'}, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + initialValue: {textFieldName: 'hi'}, + ), + ); formKey.currentState?.patchValue({textFieldName: 'test'}); await tester.pumpAndSettle(); @@ -440,14 +473,17 @@ void main() { expect(formKey.currentState?.isDirty, false); }); - testWidgets('Should not dirty when reset field with initial value', - (tester) async { + testWidgets('Should not dirty when reset field with initial value', ( + tester, + ) async { const textFieldName = 'text'; final testWidget = FormBuilderTextField(name: textFieldName); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: {textFieldName: 'hi'}, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testWidget, + initialValue: {textFieldName: 'hi'}, + ), + ); formKey.currentState?.patchValue({textFieldName: 'test'}); await tester.pumpAndSettle(); @@ -492,13 +528,10 @@ void main() { testWidgets('Should reset to initial when call reset', (tester) async { const textFieldName = 'text'; const initialValue = {textFieldName: 'test'}; - final testWidget = FormBuilderTextField( - name: textFieldName, + final testWidget = FormBuilderTextField(name: textFieldName); + await tester.pumpWidget( + buildTestableFieldWidget(testWidget, initialValue: initialValue), ); - await tester.pumpWidget(buildTestableFieldWidget( - testWidget, - initialValue: initialValue, - )); formKey.currentState?.patchValue({textFieldName: 'hello'}); await tester.pumpAndSettle(); @@ -507,26 +540,28 @@ void main() { expect(formKey.currentState?.instantValue, equals(initialValue)); }); testWidgets( - 'Should reset custom error when invalidate field and then reset', - (tester) async { - const textFieldName = 'text'; - const errorTextField = 'error text field'; - final testWidget = FormBuilderTextField(name: textFieldName); - await tester.pumpWidget(buildTestableFieldWidget(testWidget)); + 'Should reset custom error when invalidate field and then reset', + (tester) async { + const textFieldName = 'text'; + const errorTextField = 'error text field'; + final testWidget = FormBuilderTextField(name: textFieldName); + await tester.pumpWidget(buildTestableFieldWidget(testWidget)); - formKey.currentState?.fields[textFieldName]?.invalidate(errorTextField); - await tester.pumpAndSettle(); + formKey.currentState?.fields[textFieldName]?.invalidate(errorTextField); + await tester.pumpAndSettle(); - // Reset custom error - formKey.currentState?.reset(); - await tester.pumpAndSettle(); - expect(find.text(errorTextField), findsNothing); - }); + // Reset custom error + formKey.currentState?.reset(); + await tester.pumpAndSettle(); + expect(find.text(errorTextField), findsNothing); + }, + ); }); group('errors -', () { - testWidgets('Should get errors when more than one fields are invalid', - (tester) async { + testWidgets('Should get errors when more than one fields are invalid', ( + tester, + ) async { const textFieldName = 'text'; const checkboxName = 'checkbox'; const textFieldError = 'error text'; @@ -540,17 +575,16 @@ void main() { name: checkboxName, validator: (value) => checkboxError, ); - await tester.pumpWidget(buildTestableFieldWidget( - Column(children: [testTextField, testCheckbox]), - autovalidateMode: AutovalidateMode.always, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + Column(children: [testTextField, testCheckbox]), + autovalidateMode: AutovalidateMode.always, + ), + ); expect( formKey.currentState?.errors, - equals({ - textFieldName: textFieldError, - checkboxName: checkboxError, - }), + equals({textFieldName: textFieldError, checkboxName: checkboxError}), ); }); testWidgets('Should get errors when one field are invalid', (tester) async { @@ -560,26 +594,29 @@ void main() { name: textFieldName, validator: (value) => textFieldError, ); - await tester.pumpWidget(buildTestableFieldWidget( - testTextField, - autovalidateMode: AutovalidateMode.always, - )); + await tester.pumpWidget( + buildTestableFieldWidget( + testTextField, + autovalidateMode: AutovalidateMode.always, + ), + ); expect( formKey.currentState?.errors, equals({textFieldName: textFieldError}), ); }); - testWidgets('Should not get errors when all fields are valid', - (tester) async { + testWidgets('Should not get errors when all fields are valid', ( + tester, + ) async { const textFieldName = 'text'; - final testTextField = FormBuilderTextField( - name: textFieldName, + final testTextField = FormBuilderTextField(name: textFieldName); + await tester.pumpWidget( + buildTestableFieldWidget( + testTextField, + autovalidateMode: AutovalidateMode.always, + ), ); - await tester.pumpWidget(buildTestableFieldWidget( - testTextField, - autovalidateMode: AutovalidateMode.always, - )); expect(formKey.currentState?.errors, equals({})); }); @@ -589,10 +626,7 @@ void main() { // simple stateful widget that can hide and show its child with the intent of // disposing it from the tree class _DynamicFormFields extends StatefulWidget { - const _DynamicFormFields({ - required this.name, - this.valueTransformer, - }); + const _DynamicFormFields({required this.name, this.valueTransformer}); final String name; final ValueTransformer? valueTransformer; @@ -617,9 +651,7 @@ class _DynamicFormFieldsState extends State<_DynamicFormFields> { name: widget.name, valueTransformer: widget.valueTransformer, builder: (FormFieldState field) { - return TextField( - onChanged: (value) => field.didChange(value), - ); + return TextField(onChanged: (value) => field.didChange(value)); }, ), );