Skip to content

[FlutterFormBuilder]: using context.findAncestorStateOfType in FormBuilder.of #1474

Description

@dadagov125

Is there an existing issue for this?

  • I have searched the existing issues

Package/Plugin version

9.7.0

Platforms

  • Android
  • iOS
  • Linux
  • MacOS
  • Web
  • Windows

Flutter doctor

Flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel stable, 3.27.3, on macOS 15.2 24C101 darwin-arm64, locale ru-UZ)
    ✗ Downloaded executables cannot execute on host.
      See https://github.com/flutter/flutter/issues/6207 for more information.
      Flutter requires the Rosetta translation environment on ARM Macs. Try running:
        sudo softwareupdate --install-rosetta --agree-to-license

[!] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/macos-android-setup for more details.
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.2)
[✓] IntelliJ IDEA Ultimate Edition (version 2024.3.2.2)
[✓] Connected device (5 available)            
    ! Error: Browsing on the local area network for iPhone 13 Magomed . Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
[!] Network resources                                           
    ✗ A cryptographic error occurred while checking "https://pub.dev/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.
    ✗ An HTTP error occurred while checking "https://storage.googleapis.com/": Connection closed before full header was received
    ✗ A cryptographic error occurred while checking "https://maven.google.com/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.
    ✗ A network error occurred while checking "https://cocoapods.org/": Connection reset by peer
    ✗ A cryptographic error occurred while checking "https://github.com/": Connection terminated during handshake
      You may be experiencing a man-in-the-middle attack, your network may be compromised, or you may have malware installed on your computer.

! Doctor found issues in 3 categories.

Minimal code example

Code sample
                    Builder(
                      builder: (context) {
                        final formState = FormBuilder.of(context);
                      },
                    );

Current Behavior

Currently, if you call FormBuilder.of in builder, it calls context.findAncestorStateOfType() which reduces the performance of the application because findAncestorStateOfType enumerates all parent nodes in the tree until it finds FormBuilderState.
In addition, with this approach, the current element is not updated when FormBuilderState changes.

Expected Behavior

FormBuilder.of must use context.dependOnInheritedWidgetOfExactType.

  static FormBuilderState? of(BuildContext context, [bool listen = false]) {
    if (listen) {
      return context
          .dependOnInheritedWidgetOfExactType<_FormBuilderScope>()
          ._formState;
    }
    return context.findAncestorStateOfType<FormBuilderState>();
  }

Steps To Reproduce

  1. Create any widget.
  2. Wrap it in FormBuilder
  3. In the widget's build method, call FormBuilder.of(context)

Aditional information

No response

Activity

  1. added theissue type on Feb 26, 2025
  2. itsUndefined commented on Apr 17, 2025

    @itsUndefined

    @deandreamatias any chance this can get prioritized? Should be a very easy fix.

  3. deandreamatias commented on Apr 17, 2025

    @deandreamatias
    Collaborator

    @itsUndefined had a lot things to solve on https://github.com/flutter-form-builder-ecosystem and I don't have time enogth to solve all :(
    But I'm totally open to review a PR if someone propose a solution for this

  4. deandreamatias commented on Apr 21, 2025

    @deandreamatias
    Collaborator

    Hi @itsUndefined !
    I need a minimal code example to can execute:

    • Without external packages and dependencies
    • Without refereces of imports
    • With only Flutter Material or Cupertino widgets and FormBuilder widgets
    • Starting from main Dart method

    Also, I need:

    • A way to compare is a bad performance and what is a good performance.
    • How can reproduce this:

    the current element is not updated when FormBuilderState changes.

    Example:

    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: [
                FormBuilderTextField(
                  name: 'full_name',
                  decoration: const InputDecoration(labelText: 'Full Name'),
                  validator: FormBuilderValidators.compose([
                    FormBuilderValidators.required(),
                  ]),
                ),
                const SizedBox(height: 10),
                ElevatedButton(
                  onPressed: () {
                    _formKey.currentState?.saveAndValidate();
                    debugPrint(_formKey.currentState?.value.toString());
                  },
                  child: const Text('Print'),
                )
              ],
            ),
          ),
        );
      }
    }

    Thanks a lot

  5. itsUndefined commented on Apr 21, 2025

    @itsUndefined

    Thank you for the references.

    Here is a minimal example showing the original issue description, regarding the widget not updating on state update.

    import 'package:flutter/material.dart';
    import 'package:flutter_form_builder/flutter_form_builder.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,
          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: SafeArea(
            child: FormBuilder(
              key: _formKey,
              child: Column(
                children: [
                  FormBuilderTextField(
                    name: 'full_name',
                    decoration: const InputDecoration(labelText: 'Full Name'),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Please enter your full name';
                      }
                      return null;
                    },
                  ),
                  const _NestedWidget(),
                  const SizedBox(height: 10),
                  ElevatedButton(
                    onPressed: () {
                      _formKey.currentState?.saveAndValidate();
                      debugPrint(_formKey.currentState?.value.toString());
                    },
                    child: const Text('Print'),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    
    class _NestedWidget extends StatelessWidget {
      const _NestedWidget();
    
      @override
      Widget build(BuildContext context) {
        final form = FormBuilder.of(context)!;
    
        return Text(form.isValid ? 'Form is valid' : 'Form is invalid');
      }
    }

    Notice that no matter what, _NestedWidget never rebuilds and "Form is invalid" is always displayed.

    I recommend adding a listen option on the Of method with listen=false as the default to keep existing behavior as is. In a future major release we can make this default to true as this appears to be the default pattern. This will be a breaking change.

  6. deandreamatias commented on Apr 22, 2025

    @deandreamatias
    Collaborator

    I'm trying to figure this out, but it's a bit wild. I'm going to take a deeper look to see if I get any solutions.

  7. deandreamatias commented on Apr 22, 2025

    @deandreamatias
    Collaborator

    Related with #1129

  8. itsUndefined commented on Apr 22, 2025

    @itsUndefined

    Thanks for investigating this. I might find some time to troubleshoot and come up with a solution.

  9. deandreamatias commented on Apr 22, 2025

    @deandreamatias
    Collaborator

    I created a draft PR. Take a look on there branch.
    I tried to solve the issue, but the _NestedWidget still without update form isValid. Maybe need force rebuild formState in some way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions