From 604fbb018f59c9c8a309ed96d327bb51241de8a2 Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Fri, 2 May 2025 11:17:08 +0200 Subject: [PATCH 01/14] test: add test to similar fields --- example/lib/minimal_code_example.dart | 2 +- example/pubspec.lock | 4 +- pubspec.lock | 4 +- test/src/form_builder_test.dart | 55 +++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/example/lib/minimal_code_example.dart b/example/lib/minimal_code_example.dart index 99d06a5c2..3ba08f902 100644 --- a/example/lib/minimal_code_example.dart +++ b/example/lib/minimal_code_example.dart @@ -43,6 +43,7 @@ class _ExamplePageState extends State<_ExamplePage> { child: FormBuilder( key: _formKey, child: Column( + spacing: 16, children: [ FormBuilderFilterChips( decoration: const InputDecoration( @@ -77,7 +78,6 @@ class _ExamplePageState extends State<_ExamplePage> { FormBuilderValidators.maxLength(3), ]), ), - const SizedBox(height: 10), ElevatedButton( onPressed: () { _formKey.currentState?.saveAndValidate(); diff --git a/example/pubspec.lock b/example/pubspec.lock index 9ae53bb79..89ff28531 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" boolean_selector: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index 0e6bb7614..09725c745 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" boolean_selector: dependency: transitive description: diff --git a/test/src/form_builder_test.dart b/test/src/form_builder_test.dart index ff4c212cd..1f87e5b89 100644 --- a/test/src/form_builder_test.dart +++ b/test/src/form_builder_test.dart @@ -621,6 +621,61 @@ void main() { expect(formKey.currentState?.errors, equals({})); }); }); + + group('multiple fields interaction -', () { + testWidgets('Should update form builder fields when has similar fields', ( + tester, + ) async { + // Arrange + const firstDropdownName = '1_dropdown'; + const secondDropdownName = '2_dropdown'; + final firstDropdown = FormBuilderDropdown( + name: firstDropdownName, + items: const [ + DropdownMenuItem(value: 1, child: Text('1')), + DropdownMenuItem(value: 2, child: Text('2')), + DropdownMenuItem(value: 3, child: Text('3')), + ], + ); + final secondDropdown = FormBuilderDropdown( + name: secondDropdownName, + items: const [ + DropdownMenuItem(value: 1, child: Text('1')), + DropdownMenuItem(value: 2, child: Text('2')), + DropdownMenuItem(value: 3, child: Text('3')), + ], + ); + await tester.pumpWidget( + buildTestableFieldWidget( + Column(children: [firstDropdown, secondDropdown]), + ), + ); + + // Act + final firstDropdownFinder = find.byWidget(firstDropdown); + await tester.tap(firstDropdownFinder); + await tester.pumpAndSettle(); + await tester.tap(find.text('1').last); + await tester.pumpAndSettle(); + + // Assert + expect(formInstantValue(firstDropdownName), 1); + expect(formInstantValue(secondDropdownName), isNull); + + // Act + final secondDropdownFinder = find.byWidget(secondDropdown); + await tester.tap(secondDropdownFinder); + await tester.pumpAndSettle(); + await tester.tap(find.text('2').last); + await tester.pumpAndSettle(); + + // Assert + expect(formInstantValue(firstDropdownName), 1); + expect(formInstantValue(secondDropdownName), 2); + expect(formKey.currentState?.fields, contains(firstDropdownName)); + expect(formKey.currentState?.fields, contains(secondDropdownName)); + }); + }); } // simple stateful widget that can hide and show its child with the intent of From 525f65e716256595ea5fad3fcc49694ec75de1a9 Mon Sep 17 00:00:00 2001 From: mvarendorff2 Date: Thu, 5 Jun 2025 14:59:25 +0200 Subject: [PATCH 02/14] fix: dropdown-hint-missing --- lib/src/fields/form_builder_dropdown.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/fields/form_builder_dropdown.dart b/lib/src/fields/form_builder_dropdown.dart index 87f9dfacb..88a38dca4 100644 --- a/lib/src/fields/form_builder_dropdown.dart +++ b/lib/src/fields/form_builder_dropdown.dart @@ -295,6 +295,7 @@ class FormBuilderDropdown extends FormBuilderFieldDecoration { final hasValue = items.map((e) => e.value).contains(field.value); return InputDecorator( decoration: state.decoration, + isEmpty: !hasValue, child: DropdownButton( menuWidth: menuWidth, padding: padding, From b73e5034880f6f09e69591e2f9df88b49511c903 Mon Sep 17 00:00:00 2001 From: Cesare Emiliano <74298270+CesareIsHere@users.noreply.github.com> Date: Mon, 23 Jun 2025 19:59:23 +0200 Subject: [PATCH 03/14] (feat): Add readOnly parameter tuse to disable onTap --- lib/src/fields/form_builder_date_time_picker.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/fields/form_builder_date_time_picker.dart b/lib/src/fields/form_builder_date_time_picker.dart index cc378b2a2..b028f5e21 100644 --- a/lib/src/fields/form_builder_date_time_picker.dart +++ b/lib/src/fields/form_builder_date_time_picker.dart @@ -123,6 +123,8 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { final EntryModeChangeCallback? onEntryModeChanged; final bool barrierDismissible; + final bool readOnly; + /// Creates field for `Date`, `Time` and `DateTime` input FormBuilderDateTimePicker({ super.key, @@ -191,6 +193,7 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { this.selectableDayPredicate, this.anchorPoint, this.onEntryModeChanged, + this.readOnly = false, this.barrierDismissible = true, }) : super( builder: (FormFieldState field) { @@ -199,7 +202,7 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { return FocusTraversalGroup( policy: ReadingOrderTraversalPolicy(), child: TextField( - onTap: () => state.showPicker(), + onTap: readOnly ? () {} : () => state.showPicker(), textDirection: textDirection, textAlign: textAlign, textAlignVertical: textAlignVertical, From a4a85741c26dafcc8f9b565449c578eaed7eaf4b Mon Sep 17 00:00:00 2001 From: Cesare Emiliano <74298270+CesareIsHere@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:04:06 +0000 Subject: [PATCH 04/14] chore: Rename readOnly parameter to disablePicker and add docs --- lib/src/fields/form_builder_date_time_picker.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/fields/form_builder_date_time_picker.dart b/lib/src/fields/form_builder_date_time_picker.dart index b028f5e21..edb91c3f2 100644 --- a/lib/src/fields/form_builder_date_time_picker.dart +++ b/lib/src/fields/form_builder_date_time_picker.dart @@ -123,7 +123,8 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { final EntryModeChangeCallback? onEntryModeChanged; final bool barrierDismissible; - final bool readOnly; + /// If true, disables the picker so it's not shown when the field is tapped. + final bool disablePicker; /// Creates field for `Date`, `Time` and `DateTime` input FormBuilderDateTimePicker({ @@ -193,7 +194,7 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { this.selectableDayPredicate, this.anchorPoint, this.onEntryModeChanged, - this.readOnly = false, + this.disablePicker = false, this.barrierDismissible = true, }) : super( builder: (FormFieldState field) { @@ -202,7 +203,7 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { return FocusTraversalGroup( policy: ReadingOrderTraversalPolicy(), child: TextField( - onTap: readOnly ? () {} : () => state.showPicker(), + onTap: disablePicker ? () {} : () => state.showPicker(), textDirection: textDirection, textAlign: textAlign, textAlignVertical: textAlignVertical, From b7f6980e60b68b191d198c39c2c72400af9e9f9e Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Sun, 6 Jul 2025 11:28:59 +0200 Subject: [PATCH 05/14] fix: revert beta version change --- example/pubspec.lock | 20 ++++++++++---------- lib/src/fields/form_builder_switch.dart | 2 +- lib/src/form_builder_field.dart | 6 +++++- pubspec.lock | 20 ++++++++++---------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 37e4c5e61..569a87586 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -99,26 +99,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -208,18 +208,18 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.4" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: diff --git a/lib/src/fields/form_builder_switch.dart b/lib/src/fields/form_builder_switch.dart index d46060388..e1f7b81ca 100644 --- a/lib/src/fields/form_builder_switch.dart +++ b/lib/src/fields/form_builder_switch.dart @@ -136,7 +136,7 @@ class FormBuilderSwitch extends FormBuilderFieldDecoration { field.didChange(value); } : null, - activeThumbColor: activeColor, + activeColor: activeColor, activeThumbImage: activeThumbImage, activeTrackColor: activeTrackColor, inactiveThumbColor: inactiveThumbColor, diff --git a/lib/src/form_builder_field.dart b/lib/src/form_builder_field.dart index cd807d560..0d6c58587 100644 --- a/lib/src/form_builder_field.dart +++ b/lib/src/form_builder_field.dart @@ -37,6 +37,9 @@ class FormBuilderField extends FormField { /// Called when the field value is changed. final ValueChanged? onChanged; + /// Called when the field value is reset. + final VoidCallback? onReset; + /// {@macro flutter.widgets.Focus.focusNode} final FocusNode? focusNode; @@ -51,10 +54,10 @@ class FormBuilderField extends FormField { super.restorationId, required super.builder, super.errorBuilder, - super.onReset, required this.name, this.valueTransformer, this.onChanged, + this.onReset, this.focusNode, }); @@ -225,6 +228,7 @@ class FormBuilderFieldState, T> if (_customErrorText != null) { setState(() => _customErrorText = null); } + widget.onReset?.call(); } /// Validate field diff --git a/pubspec.lock b/pubspec.lock index 4630bb6a1..260e1a1a6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -79,26 +79,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -188,18 +188,18 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.4" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: From dd2ae906d63f623ba6e2ce64a1db8e643d9f8595 Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Sun, 6 Jul 2025 11:36:16 +0200 Subject: [PATCH 06/14] feat: improve accessibility for disable picker in FormBuilderDateTimePicker --- lib/src/fields/form_builder_date_time_picker.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/fields/form_builder_date_time_picker.dart b/lib/src/fields/form_builder_date_time_picker.dart index 89f2ab779..586205d7a 100644 --- a/lib/src/fields/form_builder_date_time_picker.dart +++ b/lib/src/fields/form_builder_date_time_picker.dart @@ -204,7 +204,7 @@ class FormBuilderDateTimePicker extends FormBuilderFieldDecoration { return FocusTraversalGroup( policy: ReadingOrderTraversalPolicy(), child: TextField( - onTap: disablePicker ? () {} : () => state.showPicker(), + onTap: disablePicker ? null : () => state.showPicker(), textDirection: textDirection, textAlign: textAlign, textAlignVertical: textAlignVertical, @@ -268,7 +268,8 @@ class _FormBuilderDateTimePickerState effectiveFocusNode.onKeyEvent = (node, event) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.space && - node.hasFocus) { + node.hasFocus && + !widget.disablePicker) { showPicker(); return KeyEventResult.handled; } From ec9b465b4bc3b9190aacb097099680ffc3030fff Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Sun, 6 Jul 2025 12:15:02 +0200 Subject: [PATCH 07/14] test: fix failed test fixes because new dart format --- test_fixes/fixes_10.0.0.dart | 7 +++++-- test_fixes/fixes_10.0.0.dart.expect | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/test_fixes/fixes_10.0.0.dart b/test_fixes/fixes_10.0.0.dart index 991135042..7d6377038 100644 --- a/test_fixes/fixes_10.0.0.dart +++ b/test_fixes/fixes_10.0.0.dart @@ -39,8 +39,11 @@ class _ExamplePageState extends State<_ExamplePage> { child: Column( children: [ FormBuilderDateTimePicker( - name: 'date', resetIcon: const Icon(Icons.clear), + name: 'date', + disablePicker: true, + autofocus: true, + autocorrect: true, ), FormBuilderChoiceChip( name: 'choice_chip', @@ -50,7 +53,7 @@ class _ExamplePageState extends State<_ExamplePage> { 'Option 3', ].map((e) => FormBuilderChipOption(value: e)).toList(), ), - FormBuilderFilterChip( + FormBuilderFilterChip( maxChips: 2, decoration: const InputDecoration( labelText: 'The language of my people', diff --git a/test_fixes/fixes_10.0.0.dart.expect b/test_fixes/fixes_10.0.0.dart.expect index 5bc87a096..4cc8e31f1 100644 --- a/test_fixes/fixes_10.0.0.dart.expect +++ b/test_fixes/fixes_10.0.0.dart.expect @@ -39,6 +39,9 @@ class _ExamplePageState extends State<_ExamplePage> { children: [ FormBuilderDateTimePicker( name: 'date', + disablePicker: true, + autofocus: true, + autocorrect: true, ), FormBuilderChoiceChips( name: 'choice_chip', @@ -48,7 +51,7 @@ class _ExamplePageState extends State<_ExamplePage> { 'Option 3', ].map((e) => FormBuilderChipOption(value: e)).toList(), ), - FormBuilderFilterChips( + FormBuilderFilterChips( decoration: const InputDecoration( labelText: 'The language of my people', enabled: false, @@ -85,7 +88,7 @@ class _ExamplePageState extends State<_ExamplePage> { debugPrint(_formKey.currentState?.value.toString()); }, child: const Text('Print'), - ) + ), ], ), ), From 36b4f8761be263ca421fce3fc28e316754b05951 Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Sun, 6 Jul 2025 12:16:06 +0200 Subject: [PATCH 08/14] chore(release): 10.1.0 --- CHANGELOG.md | 8 ++++++++ example/pubspec.lock | 2 +- pubspec.yaml | 5 ++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dabfd7754..7dd733c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 10.1.0 + +* Set minimal Flutter version to 3.32.0 +* Set minimal Dart version to 3.8.0 +* Add support for errorBuilder [created on Flutter core](https://github.com/flutter/flutter/pull/162255). By @deandreamatias on [#1480](https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues/1480) +* Avoid overwrite field children ([#1365](https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues/1365)). By @deandreamatias on [#1489](https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues/1489) +* [FormBuilderDateTimePicker] Add `disabledPicker` property. By @CesareIsHere on [#1497](https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues/1497) + # 10.1.0-beta * Add support for errorBuilder [created on Flutter core](https://github.com/flutter/flutter/pull/162255). By @deandreamatias on [#1480](https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues/1480) diff --git a/example/pubspec.lock b/example/pubspec.lock index 569a87586..5df21db76 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -60,7 +60,7 @@ packages: path: ".." relative: true source: path - version: "10.1.0-beta" + version: "10.1.0" flutter_lints: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index f64b9e36b..cc4ae3b0b 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.1.0-beta +version: 10.1.0 repository: https://github.com/flutter-form-builder-ecosystem/flutter_form_builder homepage: https://github.com/flutter-form-builder-ecosystem issue_tracker: https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues @@ -8,6 +8,9 @@ topics: - form funding: - https://opencollective.com/flutter-form-builder-ecosystem +screenshots: + - description: Demonstration of the form builder in action, showing a complete form with various input fields. + path: screenshots/complete_form.gif environment: sdk: ">=3.8.0 <4.0.0" From 0aa938c4d936b320ed2b9296f1aff907bf83b3bf Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Fri, 15 Aug 2025 11:44:24 +0200 Subject: [PATCH 09/14] build: update flutter and dart min version --- example/pubspec.lock | 32 ++++++++++++++++---------------- example/pubspec.yaml | 6 +++--- pubspec.lock | 28 ++++++++++++++-------------- pubspec.yaml | 6 +++--- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 5df21db76..78be33dca 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -83,10 +83,10 @@ packages: dependency: "direct main" description: name: form_builder_validators - sha256: cd617fa346250293ff3e2709961d0faf7b80e6e4f0ff7b500126b28d7422dd67 + sha256: "1b03c74d1db740890e6af803b43e5ebe56f8fa1ff5609cbf744e8d980dc5f8c6" url: "https://pub.dev" source: hosted - version: "11.1.2" + version: "11.2.0" intl: dependency: "direct main" description: @@ -99,26 +99,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -208,26 +208,26 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index f1f0de895..fc0c15514 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,15 +4,15 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=3.8.0 <4.0.0" - flutter: ">=3.32.0" + sdk: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" dependencies: flutter: sdk: flutter flutter_form_builder: path: ../ - form_builder_validators: ^11.1.2 + form_builder_validators: ^11.2.0 flutter_localizations: sdk: flutter intl: ">=0.20.0 <0.21.0" diff --git a/pubspec.lock b/pubspec.lock index 260e1a1a6..6766599b4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -79,26 +79,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -188,26 +188,26 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" vector_math: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "15.0.2" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index cc4ae3b0b..5933ae0e3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,14 +13,14 @@ screenshots: path: screenshots/complete_form.gif environment: - sdk: ">=3.8.0 <4.0.0" - flutter: ">=3.32.0" + sdk: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" dependencies: flutter: sdk: flutter # This version would be max, the same version used on flutter_localizations - # https://github.com/flutter/flutter/blob/17025dd88227cd9532c33fa78f5250d548d87e9a/packages/flutter_localizations/pubspec.yaml#L14 + # https://github.com/flutter/flutter/blob/stable/packages/flutter_localizations/pubspec.yaml intl: ">=0.20.0 <0.21.0" dev_dependencies: From 7694bfc9089c59c09715103941a28220fc5f440b Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Fri, 15 Aug 2025 11:44:44 +0200 Subject: [PATCH 10/14] feat: replace deprecated properties --- lib/src/fields/form_builder_switch.dart | 2 +- lib/src/form_builder_field.dart | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/src/fields/form_builder_switch.dart b/lib/src/fields/form_builder_switch.dart index e1f7b81ca..d46060388 100644 --- a/lib/src/fields/form_builder_switch.dart +++ b/lib/src/fields/form_builder_switch.dart @@ -136,7 +136,7 @@ class FormBuilderSwitch extends FormBuilderFieldDecoration { field.didChange(value); } : null, - activeColor: activeColor, + activeThumbColor: activeColor, activeThumbImage: activeThumbImage, activeTrackColor: activeTrackColor, inactiveThumbColor: inactiveThumbColor, diff --git a/lib/src/form_builder_field.dart b/lib/src/form_builder_field.dart index 0d6c58587..cd807d560 100644 --- a/lib/src/form_builder_field.dart +++ b/lib/src/form_builder_field.dart @@ -37,9 +37,6 @@ class FormBuilderField extends FormField { /// Called when the field value is changed. final ValueChanged? onChanged; - /// Called when the field value is reset. - final VoidCallback? onReset; - /// {@macro flutter.widgets.Focus.focusNode} final FocusNode? focusNode; @@ -54,10 +51,10 @@ class FormBuilderField extends FormField { super.restorationId, required super.builder, super.errorBuilder, + super.onReset, required this.name, this.valueTransformer, this.onChanged, - this.onReset, this.focusNode, }); @@ -228,7 +225,6 @@ class FormBuilderFieldState, T> if (_customErrorText != null) { setState(() => _customErrorText = null); } - widget.onReset?.call(); } /// Validate field From 30aa93e3b64af964b6097578e5989d11609cf4ba Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Fri, 15 Aug 2025 12:06:52 +0200 Subject: [PATCH 11/14] feat: update FormBuilderRadioGroup with new RadioGroup widget --- lib/src/fields/form_builder_radio_group.dart | 6 +- lib/src/widgets/grouped_radio.dart | 67 ++++++++++--------- .../fields/form_builder_radio_group_test.dart | 46 +++++++++++++ 3 files changed, 84 insertions(+), 35 deletions(-) diff --git a/lib/src/fields/form_builder_radio_group.dart b/lib/src/fields/form_builder_radio_group.dart index a27d46ed2..ff83c454c 100644 --- a/lib/src/fields/form_builder_radio_group.dart +++ b/lib/src/fields/form_builder_radio_group.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; - -import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_form_builder/src/form_builder_field.dart'; +import 'package:flutter_form_builder/src/form_builder_field_decoration.dart'; +import 'package:flutter_form_builder/src/form_builder_field_option.dart'; +import 'package:flutter_form_builder/src/widgets/grouped_radio.dart'; /// Field to select one value from a list of Radio Widgets class FormBuilderRadioGroup extends FormBuilderFieldDecoration { diff --git a/lib/src/widgets/grouped_radio.dart b/lib/src/widgets/grouped_radio.dart index d58dc4f33..af65690df 100644 --- a/lib/src/widgets/grouped_radio.dart +++ b/lib/src/widgets/grouped_radio.dart @@ -217,36 +217,42 @@ class _GroupedRadioState extends State> { widgetList.add(buildItem(i)); } - return switch (widget.orientation) { - OptionsOrientation.auto => OverflowBar( - alignment: MainAxisAlignment.spaceEvenly, - children: widgetList, - ), - OptionsOrientation.vertical => SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return RadioGroup( + onChanged: (value) { + widget.onChanged(value); + }, + groupValue: widget.value, + child: switch (widget.orientation) { + OptionsOrientation.auto => OverflowBar( + alignment: MainAxisAlignment.spaceEvenly, children: widgetList, ), - ), - OptionsOrientation.horizontal => SingleChildScrollView( - 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, + OptionsOrientation.vertical => SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: widgetList, + ), + ), + OptionsOrientation.horizontal => SingleChildScrollView( + 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, + ), ), - ), - }; + }, + ); } /// the composite of all the components for the option at index @@ -255,17 +261,12 @@ class _GroupedRadioState extends State> { final optionValue = option.value; final isOptionDisabled = true == widget.disabled?.contains(optionValue); final control = Radio( - groupValue: widget.value, activeColor: widget.activeColor, focusColor: widget.focusColor, hoverColor: widget.hoverColor, materialTapTargetSize: widget.materialTapTargetSize, value: optionValue, - onChanged: isOptionDisabled - ? null - : (T? selected) { - widget.onChanged(selected); - }, + enabled: !isOptionDisabled, ); final label = GestureDetector( diff --git a/test/src/fields/form_builder_radio_group_test.dart b/test/src/fields/form_builder_radio_group_test.dart index 27d2cdb34..703fae8fa 100644 --- a/test/src/fields/form_builder_radio_group_test.dart +++ b/test/src/fields/form_builder_radio_group_test.dart @@ -172,5 +172,51 @@ void main() { expect(Focus.of(tester.element(widgetFinder)).hasFocus, true); expect(focusNode?.hasFocus, true); }); + testWidgets( + 'when change value, onChange will be called with value changed', + (WidgetTester tester) async { + const widgetName = 'key'; + int? changedValue; + final testWidget = FormBuilderRadioGroup( + name: widgetName, + onChanged: (value) { + changedValue = value; + }, + 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(formValue(widgetName), isNull); + await tester.tap(find.byKey(const ValueKey('2'))); + await tester.pumpAndSettle(); + expect(changedValue, equals(2)); + }, + ); + testWidgets('when is disable then can not change value', ( + WidgetTester tester, + ) async { + const widgetName = 'key'; + int? changedValue; + final testWidget = FormBuilderRadioGroup( + name: widgetName, + onChanged: (value) { + changedValue = value; + }, + enabled: false, + 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)); + await tester.tap(find.byKey(const ValueKey('2'))); + await tester.pumpAndSettle(); + expect(changedValue, equals(null)); + }); }); } From e2f24d9a451f1b4520e0bf5126fb1c81f4e5c89e Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Fri, 15 Aug 2025 12:17:56 +0200 Subject: [PATCH 12/14] test: add missing tests on extensions --- .../autovalidatemode_extension_test.dart | 33 +++++++++ .../extensions/generic_validator_test.dart | 67 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 test/src/extensions/autovalidatemode_extension_test.dart create mode 100644 test/src/extensions/generic_validator_test.dart diff --git a/test/src/extensions/autovalidatemode_extension_test.dart b/test/src/extensions/autovalidatemode_extension_test.dart new file mode 100644 index 000000000..bbd8ba488 --- /dev/null +++ b/test/src/extensions/autovalidatemode_extension_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_form_builder/src/extensions/autovalidatemode_extension.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('AutoValidateModeExtension', () { + test('when mode is always then return true', () { + final mode = AutovalidateMode.always; + final result = mode.isAlways; + expect(result, isTrue); + }); + test('when mode is onUserInteraction then return true', () { + final mode = AutovalidateMode.onUserInteraction; + final result = mode.isOnUserInteraction; + expect(result, isTrue); + }); + test('when mode is disabled then return false', () { + final mode = AutovalidateMode.disabled; + final result = mode.isEnable; + expect(result, isFalse); + }); + test('when mode is always then isEnable should return true', () { + final mode = AutovalidateMode.always; + final result = mode.isEnable; + expect(result, isTrue); + }); + test('when mode is onUserInteraction then isEnable should return true', () { + final mode = AutovalidateMode.onUserInteraction; + final result = mode.isEnable; + expect(result, isTrue); + }); + }); +} diff --git a/test/src/extensions/generic_validator_test.dart b/test/src/extensions/generic_validator_test.dart new file mode 100644 index 000000000..f479a2394 --- /dev/null +++ b/test/src/extensions/generic_validator_test.dart @@ -0,0 +1,67 @@ +import 'package:flutter_form_builder/src/extensions/generic_validator.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('GenericValidator -', () { + test('when type is String and value is empty then return true', () { + final String value = ''; + final result = value.emptyValidator(); + expect(result, isTrue); + }); + test('when type is String and value is not empty then return false', () { + final String value = 'not empty'; + final result = value.emptyValidator(); + expect(result, isFalse); + }); + test('when type is List and value is empty then return true', () { + final List value = []; + final result = value.emptyValidator(); + expect(result, isTrue); + }); + test('when type is List and value is not empty then return false', () { + final List value = [1, 2, 3]; + final result = value.emptyValidator(); + expect(result, isFalse); + }); + test('when type is Map and value is empty then return true', () { + final Map value = {}; + final result = value.emptyValidator(); + expect(result, isTrue); + }); + test('when type is Map and value is not empty then return false', () { + final Map value = {'key': 1}; + final result = value.emptyValidator(); + expect(result, isFalse); + }); + test('when type is Set and value is empty then return true', () { + final Set value = {}; + final result = value.emptyValidator(); + expect(result, isTrue); + }); + test('when type is Set and value is not empty then return false', () { + final Set value = {1, 2, 3}; + final result = value.emptyValidator(); + expect(result, isFalse); + }); + test('when type is Iterable and value is empty then return true', () { + final Iterable value = []; + final result = value.emptyValidator(); + expect(result, isTrue); + }); + test('when type is Iterable and value is not empty then return false', () { + final Iterable value = [1, 2, 3]; + final result = value.emptyValidator(); + expect(result, isFalse); + }); + test('when type is not and empty possible then return false', () { + final int value = 1; + final result = value.emptyValidator(); + expect(result, isFalse); + }); + test('when value is null then return true', () { + final String? value = null; + final result = value.emptyValidator(); + expect(result, isTrue); + }); + }); +} From d07e97f34da5b2a08457e5608abda2081d265336 Mon Sep 17 00:00:00 2001 From: Matias de Andrea Date: Fri, 15 Aug 2025 12:26:01 +0200 Subject: [PATCH 13/14] chore(release): 10.2.0 --- CHANGELOG.md | 9 +++++++++ example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd733c67..8c7dd3fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 10.2.0 + +* Set minimal Flutter version to 3.35.0 +* Set minimal Dart version to 3.9.0 +* Replace deprecated properties and widgets. This do not affect the public API functionality. + * [FormBuilderRadioGroup] Replace widget to use `RadioGroup`. + * [FormBuilderField] Use `onReset` from `FormField`. + * [FormBuilderSwitch] Use `activeThumbColor` to `activeColor`. + # 10.1.0 * Set minimal Flutter version to 3.32.0 diff --git a/example/pubspec.lock b/example/pubspec.lock index 78be33dca..420b92082 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -60,7 +60,7 @@ packages: path: ".." relative: true source: path - version: "10.1.0" + version: "10.2.0" flutter_lints: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5933ae0e3..724cd62b3 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.1.0 +version: 10.2.0 repository: https://github.com/flutter-form-builder-ecosystem/flutter_form_builder homepage: https://github.com/flutter-form-builder-ecosystem issue_tracker: https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/issues From d6780f497ba0465dbca2b14ac5b0836414f1c03e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:15:10 +0000 Subject: [PATCH 14/14] build(deps): bump actions/stale from 9 to 10 Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v9...v10) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '10' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/stale.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 2db8f4953..775a72dd6 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: "This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days."