Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FormBuilderDropdown]: <If onChanged Function is added, the FormBuilderDropdown stop working> #1252

Closed
1 task done
CarmonaCarlos opened this issue May 15, 2023 · 21 comments · Fixed by #1272
Closed
1 task done
Labels
bug Something isn't working

Comments

@CarmonaCarlos
Copy link

CarmonaCarlos commented May 15, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Package/Plugin version

9.0.0

Flutter doctor

Flutter doctor

[√] Flutter (Channel stable, 3.10.0, on Microsoft Windows [Version 10.0.22621.1702], locale es-MX)
• Flutter version 3.10.0 on channel stable at C:\Users\Carlos\flutter_sdk\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 84a1e904f4 (6 days ago), 2023-05-09 07:41:44 -0700
• Engine revision d44b5a94c9
• Dart version 3.0.0
• DevTools version 2.23.1

[√] Windows Version (Installed version of Windows is version 10 or higher)

[√] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
• Android SDK at C:\Users\Carlos\AppData\Local\Android\sdk
• Platform android-33, build-tools 31.0.0
• Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
• Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-9586694)
• All Android licenses accepted.

[√] Chrome - develop for the web
• Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.5.5)
• Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
• Visual Studio Community 2022 version 17.5.33627.172
• Windows 10 SDK version 10.0.22621.0

[√] Android Studio (version 2022.2)
• Android Studio at C:\Program Files\Android\Android Studio
• Flutter plugin can be installed from:
https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-9586694)

[√] VS Code (version 1.78.2)
• VS Code at C:\Users\Carlos\AppData\Local\Programs\Microsoft VS Code
• Flutter extension version 3.64.0

[√] Connected device (3 available)
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.22621.1702]
• Chrome (web) • chrome • web-javascript • Google Chrome 113.0.5672.93
• Edge (web) • edge • web-javascript • Microsoft Edge 113.0.1774.42

[√] Network resources
• All expected network resources are available.

• No issues found!

Minimal code example

Code sample
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({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter FormBuilder Demo',
      debugShowCheckedModeBanner: false,
      localizationsDelegates: [
        FormBuilderLocalizations.delegate,
        ...GlobalMaterialLocalizations.delegates,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: FormBuilderLocalizations.supportedLocales,
      home: Example(),
    );
  }
}

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

  @override
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  int? selectedLineId;
  final GlobalKey<FormBuilderState> _formKey = GlobalKey<FormBuilderState>();
  late List<Line> lines;

  @override
  void initState() {
    lines = _initLines();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Example')),
      body: FormBuilder(
        key: _formKey,
        child: Column(
          children: <Widget>[
            FormBuilderDropdown<int>(
              name: 'lineId',
              valueTransformer: (int? val) => val?.toInt(),
              decoration: const InputDecoration(
                  labelText: 'lines', icon: Icon(Icons.ad_units)),
              validator: FormBuilderValidators.compose(
                  [FormBuilderValidators.required()]),
              items: lines
                  .map((Line item) => DropdownMenuItem<int>(
                        value: item.id,
                        child: Text(item.name),
                      ))
                  .toList(),
              onChanged: (int? value) {
                setState(() {
                  selectedLineId = value;
                });
              },
            )
          ],
        ),
      ),
    );
  }

  List<Line> _initLines() {
    //This is just for test purpose
    return <Line>[
      Line(
        id: 1,
        name: 'Test',
      ),
      Line(
        id: 2,
        name: 'Test 2',
      )
    ];
  }
}

class Line {
  final int id;
  final String name;

  Line({required this.id, required this.name});
}

Current Behavior

If I add setState inside of onChanged the FormBuilderDropdown is not working correctly:

If I select an option:

01

Then nothing is selected:

02

On previous version it was working once I update to 9.0.0 it stop to works.

Expected Behavior

If I select an option

For example:

01

Then the option is selected:

03

Steps To Reproduce

  1. Add setState to onChanged

Aditional information

No response

@CarmonaCarlos CarmonaCarlos added the bug Something isn't working label May 15, 2023
@bull-xu
Copy link

bull-xu commented May 16, 2023

The setState triggers the rebuild. and a break point to the didUpdateWidget showing the initialValue has been set to value:

image

To fix the issue, a walkaround is to set the items at initState.

@deandreamatias
Copy link
Collaborator

I test the example code on web with Flutter 3.10 and works fine. Please edit minimal example code or add more details about the bug

@deandreamatias deandreamatias added the awaiting author response Waiting for author of issue to respond with more info label May 18, 2023
@eaedev
Copy link

eaedev commented May 19, 2023

Same problem here. If you use a setState in onChanged, then the widget get rebuilds and it jumps to the initial value

@tetious
Copy link

tetious commented May 21, 2023

@deandreamatias I think the key here is that the form needs to rebuild to trigger. It probably won't happen as easily on the web, but on mobile the widget rebuilds when the input/keyboard changes, and that is pretty common on a form.

If you pick something in the dropdown (keyboard goes away) and then move focus to a widget that reopens the keyboard, the selected item dissapears.

@GiuseppeSperanza
Copy link

Yes, same problem. Any update?

@mngphm
Copy link

mngphm commented May 30, 2023

same problem, will this be fixed? or any workaround?

@tetious
Copy link

tetious commented May 30, 2023

My workaround (which may not be the best, but seems to work) is to set the model prop value manually in onChanged, kinda like this:

onChanged: (value) => YOUR_MODEL.whateverProp = value,

@ncuillery
Copy link

More generally, rebuilding the widget where the FormBuilderDropdown is declared causes the following error if the items prop is reassigned:

The following assertion was thrown building MyForm:
setState() or markNeedsBuild() called during build.

This Form widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.

The workaround consists in keeping the items array (the same reference) by declaring the array of DropdownMenuItem in the initState, like @bull-xu mentioned for instance

@Dubemtopsite
Copy link

Facing same issue while using setState to update some variables from onChanged

@Dubemtopsite
Copy link

My workaround (which may not be the best, but seems to work) is to set the model prop value manually in onChanged, kinda like this:

onChanged: (value) => YOUR_MODEL.whateverProp = value,

Had to create a model for my form and update the model from the onChanged function

@deandreamatias deandreamatias removed the awaiting author response Waiting for author of issue to respond with more info label Jun 12, 2023
@hallmayer

This comment was marked as spam.

@godofsleepy
Copy link

Screen.Recording.2023-06-20.at.11.26.36.mov

Version 9.0.0
Got same error when on change trigger GetX builder(form key.did change -> getx).
then when i downgrade to version 8.0.0 its fixed, any suggest ?

@ashburger
Copy link

ashburger commented Jun 21, 2023

Version 9.0.0

(On Android)
I get a similar issue, where if I use a dynamic list for items, when setState is called in the form onChanged, the dropdown with the dynamic list is treated as invalid. It should be noted, that if you check the form's formKey.currentstate.fields['dynamicDropdown'].value, it does show the correct value, and the list itself displays correctly.
Another issue is that if in the form, the first entry you change is the dynamic dropdown, and setstate is called, the dropdown resets itself, but only once, if you select an item in it again, it displays the selection correctly, but is still considered invalid.

EDIT: with this block
WidgetsBinding.instance.addPostFrameCallback((_) { if (_formKey.currentState?.isValid ?? false) { setState(() { //todo }); } }
the dynamic dropdown is always reset
These issues don't affect dropdowns with const item lists

@ShashankSMayya
Copy link

ShashankSMayya commented Jun 30, 2023

Any updates on this? in Form builder 9.0 this issue is persistent. The form builder values reset to initial values on reset. For now my solution is to use Form Builder 8.0.

Is it something to do with this changelog point?

  • "Improve FormBuilder rebuild. Now only rebuild at same time the field that user touch, not all touched fields"

@latenite-ironman
Copy link

I hope this will be fixed soon. because of this issue, we cannot update to the latest flutter version 3.10.

@ShashankSMayya
Copy link

I hope this will be fixed soon. because of this issue, we cannot update to the latest flutter version 3.10.

You can override dependency to upgrade to latest version of flutter, I am using 3.10 with other package with overridden intl package.

Anyways I hope the team solves this issue soon.

@danvick
Copy link
Collaborator

danvick commented Jul 3, 2023

The setState triggers the rebuild. and a break point to the didUpdateWidget showing the initialValue has been set to value:

image

To fix the issue, a walkaround is to set the items at initState.

@deandreamatias, I believe the bug originates from the didUpdateWidget() function as highlighted by @bull-xu.

I understand the reason for adding the functionality was to avoid the failed assertion where if the items List is dynamically changed and the new list doesn't contain the already selected value.

I think the method should be removed because of the following reasons:

  1. We are not performing a deep equality comparison, therefore, widget.items != oldWidget.items will almost always return true. Remember that in Dart [1,2,3] != [1,2,3] is true!
  2. Even if we did deep comparison of the list and the lists are of non-primitive types which don't override the equality operator, the comparison would still fail.
  3. If the initialValue of the field is non-null but it's not part of the new updated items List, the assertion will still fail.
  4. Since what we are trying to avoid is just an assertion, we can let the users of the library handle that.

@Rileyjrjohns
Copy link

Hello, any updates on the subject?
It's quite disturbing. For example, I wanted to create a pseudonym selector with an "Other" field that would display a text field conditionally. Set State doesn't work, but neither does riverpod (which works on SetState) for this type of task. It's a pity, because there's no way out for this kind of functionality...

sarbogast added a commit to scorelyr/flutter_form_builder that referenced this issue Jul 11, 2023
@deandreamatias
Copy link
Collaborator

Hi people!
Sorry for can't take a look on this issue before. I had a lot of things happening and also had a bug with my Android install and emulator, that I solved today (3 AM).
This issue is my first priority and I expect work on bug ASAP.

All the comments that you have added and can add, will be useful for debug.
Thanks for patience

@deandreamatias
Copy link
Collaborator

@deandreamatias I think the key here is that the form needs to rebuild to trigger. It probably won't happen as easily on the web, but on mobile the widget rebuilds when the input/keyboard changes, and that is pretty common on a form.

If you pick something in the dropdown (keyboard goes away) and then move focus to a widget that reopens the keyboard, the selected item dissapears.

I think that the problem is related when use autovalidateMode: AutovalidateMode.onUserInteraction

@deandreamatias
Copy link
Collaborator

I opened a PR. Please if all of you can review this PR and considerations on it, I will really appreciate it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.