Repository: flutter-form-builder-ecosystem/flutter_form_builder

Issue:
[General]: When decoration is disabled, form field is enabled
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Package/Plugin version

9.7.0

### Platforms

- [X] Android
- [X] iOS
- [X] Linux
- [X] MacOS
- [X] Web
- [X] Windows

### Flutter doctor

<details>
  <summary>Flutter doctor</summary>

<!-- Paste the output below inside ``` and ``` -->
```bash
[✓] Flutter (Channel stable, 3.27.1, on Ubuntu 24.04.1 LTS 6.8.0-51-generic, locale en_US.UTF-8)
    • Flutter version 3.27.1 on channel stable at /home/matias/fvm/versions/stable
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 17025dd882 (3 weeks ago), 2024-12-17 03:23:09 +0900
    • Engine revision cb4b5fff73
    • Dart version 3.6.0
    • DevTools version 2.40.2

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0-rc4)
    • Android SDK at /home/matias/Android/Sdk
    • Platform android-35, build-tools 35.0.0-rc4
    • Java binary at: /home/matias/.local/share/JetBrains/Toolbox/apps/android-studio/jbr/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • CHROME_EXECUTABLE = /usr/bin/google-chrome

[✗] Linux toolchain - develop for Linux desktop
    ✗ clang++ is required for Linux development.
      It is likely available from your distribution (e.g.: apt install clang), or can be downloaded from https://releases.llvm.org/
    • cmake version 3.28.3
    • ninja version 1.11.1
    • pkg-config version 1.8.1

[✓] Android Studio (version 2024.1)
    • Android Studio at /home/matias/.local/share/JetBrains/Toolbox/apps/android-studio
    • Flutter plugin version 81.0.2
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.11+0-17.0.11b1207.24-11852314)

[✓] VS Code (version 1.96.1)
    • VS Code at /snap/code/current/usr/share/code
    • Flutter extension version 3.102.0

[✓] Connected device (2 available)
    • Linux (desktop) • linux  • linux-x64      • Ubuntu 24.04.1 LTS 6.8.0-51-generic
    • Chrome (web)    • chrome • web-javascript • Google Chrome 110.0.5481.177

[✓] Network resources
    • All expected network resources are available.
```

</details>


### Minimal code example

<details>
  <summary>Code sample</summary>

<!-- Paste the output below inside ``` and ``` -->

```dart
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:form_builder_validators/form_builder_validators.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter FormBuilder Example',
      debugShowCheckedModeBanner: false,
      localizationsDelegates: const [
        FormBuilderLocalizations.delegate,
        ...GlobalMaterialLocalizations.delegates,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: FormBuilderLocalizations.supportedLocales,
      home: const _ExamplePage(),
    );
  }
}

class _ExamplePage extends StatefulWidget {
  const _ExamplePage();

  @override
  State<_ExamplePage> createState() => _ExamplePageState();
}

class _ExamplePageState extends State<_ExamplePage> {
  final _formKey = GlobalKey<FormBuilderState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FormBuilder(
        key: _formKey,
        child: Column(
          children: [
            FormBuilderFilterChip<String>(
              decoration: const InputDecoration(
                labelText: 'The language of my people',
                enabled: false, // <--- Property changed
              ),
              name: 'languages_filter',
              selectedColor: Colors.red,
              options: const [
                FormBuilderChipOption(
                  value: 'Dart',
                  avatar: CircleAvatar(child: Text('D')),
                ),
                FormBuilderChipOption(
                  value: 'Kotlin',
                  avatar: CircleAvatar(child: Text('K')),
                ),
                FormBuilderChipOption(
                  value: 'Java',
                  avatar: CircleAvatar(child: Text('J')),
                ),
                FormBuilderChipOption(
                  value: 'Swift',
                  avatar: CircleAvatar(child: Text('S')),
                ),
                FormBuilderChipOption(
                  value: 'Objective-C',
                  avatar: CircleAvatar(child: Text('O')),
                ),
              ],
              validator: FormBuilderValidators.compose([
                FormBuilderValidators.minLength(1),
                FormBuilderValidators.maxLength(3),
              ]),
            ),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                _formKey.currentState?.saveAndValidate();
                debugPrint(_formKey.currentState?.value.toString());
              },
              child: const Text('Print'),
            )
          ],
        ),
      ),
    );
  }
}
```
</details>


### Current Behavior

When set a decoration property with a InputDecoration(enabled: false), the field still enabled

### Expected Behavior

When set a `decoration` property with a `InputDecoration(enabled: false)`, the field will be disabled and maintain consistency with field property `enabled` and form builder property `enabled` 

### Steps To Reproduce

1. Run example
2. See the field enabled or focus the field 

### Aditional information

Error discovered when implement changes on https://github.com/flutter-form-builder-ecosystem/flutter_form_builder/pull/1453


Discussion: Error related on Flutter: https://github.com/flutter/flutter/issues/161602

Fix patch:
diff --git a/lib/src/form_builder_field_decoration.dart b/lib/src/form_builder_field_decoration.dart
index 7fb636caa..070fcf375 100644
--- a/lib/src/form_builder_field_decoration.dart
+++ b/lib/src/form_builder_field_decoration.dart
@@ -6,7 +6,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
 ///
 /// This class override `decoration.enable` with [enable] value
 class FormBuilderFieldDecoration<T> extends FormBuilderField<T> {
-  const FormBuilderFieldDecoration({
+  FormBuilderFieldDecoration({
     super.key,
     super.onSaved,
     super.initialValue,
@@ -21,7 +21,12 @@ class FormBuilderFieldDecoration<T> extends FormBuilderField<T> {
     super.focusNode,
     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.
+            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.''');
   final InputDecoration decoration;
 
   @override
@@ -40,7 +45,9 @@ class FormBuilderFieldDecorationState<F extends FormBuilderFieldDecoration<T>,
         errorText: widget.enabled || readOnly
             ? widget.decoration.errorText ?? errorText
             : null,
-        enabled: widget.enabled,
+        enabled: widget.decoration.enabled
+            ? widget.enabled
+            : widget.decoration.enabled,
       );
 
   @override


Test patch:
diff --git a/test/src/form_builder_field_decoration_test.dart b/test/src/form_builder_field_decoration_test.dart
new file mode 100644
index 000000000..744248cd9
--- /dev/null
+++ b/test/src/form_builder_field_decoration_test.dart
@@ -0,0 +1,187 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_form_builder/flutter_form_builder.dart';
+
+import '../form_builder_tester.dart';
+
+void main() {
+  group('FormBuilderFieldDecoration -', () {
+    testWidgets('when change the error text then the field should be invalid',
+        (WidgetTester tester) async {
+      final decorationFieldKey = GlobalKey<FormBuilderFieldDecorationState>();
+      const name = 'testField';
+      const errorTextField = 'error text field';
+      final widget = FormBuilderFieldDecoration<String>(
+        key: decorationFieldKey,
+        name: name,
+        builder: (FormFieldState<String?> 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);
+    });
+    group('decoration enabled -', () {
+      testWidgets('when change the error text then the field should be invalid',
+          (WidgetTester tester) async {
+        final decorationFieldKey = GlobalKey<FormBuilderFieldDecorationState>();
+        const name = 'testField';
+        const errorTextField = 'error text field';
+        final widget = FormBuilderFieldDecoration<String>(
+          key: decorationFieldKey,
+          name: name,
+          builder: (FormFieldState<String?> 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<FormBuilderFieldDecorationState>();
+        const name = 'testField';
+
+        expect(
+          () => FormBuilderFieldDecoration<String>(
+            key: decorationFieldKey,
+            name: name,
+            decoration: const InputDecoration(enabled: false),
+            builder: (FormFieldState<String?> 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<FormBuilderFieldDecorationState>();
+        const name = 'testField';
+
+        expect(
+          () => FormBuilderFieldDecoration<String>(
+            key: decorationFieldKey,
+            name: name,
+            decoration: const InputDecoration(enabled: false),
+            enabled: false,
+            builder: (FormFieldState<String?> 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<FormBuilderFieldDecorationState>();
+        const name = 'testField';
+
+        expect(
+          () => FormBuilderFieldDecoration<String>(
+            key: decorationFieldKey,
+            name: name,
+            builder: (FormFieldState<String?> 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<FormBuilderFieldDecorationState>();
+        const name = 'testField';
+
+        expect(
+          () => FormBuilderFieldDecoration<String>(
+            key: decorationFieldKey,
+            name: name,
+            enabled: false,
+            builder: (FormFieldState<String?> field) {
+              return InputDecorator(
+                decoration:
+                    (field as FormBuilderFieldDecorationState).decoration,
+                child: TextField(
+                  onChanged: (value) {
+                    field.didChange(value);
+                  },
+                ),
+              );
+            },
+          ),
+          returnsNormally,
+        );
+      });
+    });
+  });
+}