Home > Software design >  Dynamic DropDownFormField returns null on Submit Button
Dynamic DropDownFormField returns null on Submit Button

Time:01-29

I have created a page where user can input their exam result which are subject and grade.

On the page, user need to click the add subject button to add another subject depending on how many subject they have. User also can remove subject by clicking the remove subject button.

The problem is only TextFormField value submitted. How to get the data for DropDownFormField if the form created dynamically?

I have tried the read the data using the onChanged function of the drop down and yes the data was stored to the variable gradeController but it returns null when I submit the data.

This is the model

class SubjectGrade {
  final String? name;
  final String? grade;

  SubjectGrade(this.name, this.grade);

  @override
  String toString() {
    return 'SPM: name = $name, grade = $grade';
  }
}

This is the form

import 'package:flutter/material.dart';
import 'package:testing_app/models/education_model.dart';

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

  @override
  _EducationBackgroundFormState createState() =>
      _EducationBackgroundFormState();
}

class _EducationBackgroundFormState extends State<EducationBackgroundForm> {
  final nameTECs = <TextEditingController>[];
  final gradeTECs = <String?>[];
  final forms = <Form>[];

  Form createForm() {
    TextEditingController nameController = TextEditingController();
    String? gradeController;
    nameTECs.add(nameController);
    gradeTECs.add(gradeController);

    final grades = ['A ', 'A', 'A-', 'B ', 'B', 'C ', 'C', 'D', 'E', 'G'];

    DropdownMenuItem<String> buildMenuItem(String item) => DropdownMenuItem(
          value: item,
          child: Text(item),
        );

    return Form(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Row(
            children: <Widget>[
              Expanded(
                flex: 2,
                child: Column(
                  children: <Widget>[
                    TextFormField(
                      controller: nameController,
                      decoration: InputDecoration(
                        hintText: 'Subject Name',
                        labelText: 'Subject ${forms.length   1}',
                        border: const OutlineInputBorder(),
                        contentPadding:
                            const EdgeInsets.only(left: 8, right: 8),
                      ),
                      keyboardType: TextInputType.text,
                      textInputAction: TextInputAction.done,
                    ),
                  ],
                ),
              ),
              const SizedBox(width: 5),
              Expanded(
                  flex: 1,
                  child: Column(
                    children: <Widget>[
                      DropdownButtonHideUnderline(
                        child: DropdownButtonFormField(
                          value: gradeController,
                          isExpanded: true,
                          iconSize: 36,
                          icon: const Icon(Icons.arrow_drop_down,
                              color: Colors.black),
                          items: grades.map(buildMenuItem).toList(),
                          onChanged: (String? value) {
                            setState(() {
                              gradeController = value;
                            });
                          },
                          hint: const Text("Grade"),
                          decoration: const InputDecoration(
                            border: OutlineInputBorder(),
                            contentPadding: EdgeInsets.only(left: 8),
                          ),
                        ),
                      )
                    ],
                  )),
            ],
          ),
          const SizedBox(height: 10)
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    forms.add(createForm());
  }

  saveSPMResultToFirebase() {
    List<SubjectGrade> entries = [];
    for (int i = 0; i < forms.length; i  ) {
      final name = nameTECs[i].text;
      final grade = gradeTECs[i].toString();
      entries.add(SubjectGrade(name, grade));
    }
    debugPrint(entries.toString());
    //Navigator.pop(context, entries);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        const Text("SPM Result", style: TextStyle(fontSize: 18)),
        const SizedBox(height: 20),
        Expanded(
          child: ListView.builder(
            itemCount: forms.length,
            itemBuilder: (BuildContext context, int index) {
              return forms[index];
            },
          ),
        ),
        const SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: SizedBox(
                width: double.infinity,
                child: ElevatedButton(
                  style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.all(14),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(8))),
                  onPressed: () => setState(() {
                    forms.add(createForm());
                  }),
                  child: const Text(
                    'Add Subject',
                    style: TextStyle(
                      fontSize: 16,
                    ),
                  ),
                ),
              ),
            ),
            const SizedBox(width: 5),
            Expanded(
              child: SizedBox(
                width: double.infinity,
                child: ElevatedButton(
                  style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.all(14),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(8))),
                  onPressed: () => setState(() {
                    if (forms.length > 1) {
                      forms.removeAt(forms.length - 1);
                    }
                  }),
                  child: const Text(
                    'Remove Subject',
                    style: TextStyle(
                      fontSize: 16,
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 10),
        SizedBox(
          width: double.infinity,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.all(14),
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8))),
            onPressed: () {
              saveSPMResultToFirebase();
            },
            child: const Text(
              'Update',
              style: TextStyle(
                fontSize: 16,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

When I submit the form, the debug output I get from the submit function for grade is null.

Output:

[SPM: name = English, grade = null]

CodePudding user response:

You should add value of DropDownButton in onChanged method not at top of create form. change your code to this:

First Delete gradeTECs.add(gradeController); line from top of createForm().

Form createForm() {
    TextEditingController nameController = TextEditingController();
    String? gradeController;
    nameTECs.add(nameController);
    // gradeTECs.add(gradeController);  <==== DELETE THIS LINE.

Add gradeTECs.add(gradeontroller); in onChanged method of DropdownButtonFormField

DropdownButtonHideUnderline(
                      child: DropdownButtonFormField(
                        value: gradeController,
                        isExpanded: true,
                        iconSize: 36,
                        icon: const Icon(Icons.arrow_drop_down,
                            color: Colors.black),
                        items: grades.map(buildMenuItem).toList(),
                        onChanged: (String? value) {
                          setState(() {
                            gradeController = value;
                            gradeTECs.add(gradeController); <=== ADD HERE ==>
                          });
                        },
                        hint: const Text("Grade"),
                        decoration: const InputDecoration(
                          border: OutlineInputBorder(),
                          contentPadding: EdgeInsets.only(left: 8),
                        ),
                      ),
                    ),
  •  Tags:  
  • Related