'flutter create dynamic TextField when button click

enter image description here

Here is my requirement, when I click the Add button, dynamically new cards with three TextFields should be generated, and how to assign each TextField with dynamically created TextEditingControllers> or is there any other way to take value from TextFields?

final name1 = new TextField(
    controller: name1Controller,
    decoration: InputDecoration(
        labelText: 'Full Name', border: OutlineInputBorder()));

final age1 = new TextField(
    controller: age1Controler,
    keyboardType: TextInputType.number,
    decoration:
        InputDecoration(labelText: 'Age', border: OutlineInputBorder()));

final studyjob1 = new TextField(
    controller: study1Controller,
    decoration: InputDecoration(
        labelText: 'Study / Job', border: OutlineInputBorder()));

final person1Card = new Card(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(15.0),
  ),
  elevation: 10,
  child: Padding(
    padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
    child: Column(
      children: <Widget>[
        Text('Person 1'),
        SizedBox(height: 3.0),
        name1,
        SizedBox(height: 10.0),
        age1,
        SizedBox(height: 10.0),
        studyjob1,
        SizedBox(height: 10.0),
      ],
    ),
  ),
);

return Scaffold(
    appBar: AppBar(
      title: Text('New Entry'),
    ),
    body: SingleChildScrollView(
      child: Container(
        child: Container(
          color: Colors.white,
          child: Padding(
            padding: const EdgeInsets.all(15.0),
            child: Column(
              children: <Widget>[
                person1Card,
                SizedBox(
                  height: 10.0,
                ),
                saveButton
              ],
            ),
          ),
        ),
      ),
    ))


Solution 1:[1]

You can use a List for your controllers.

For example:

class PersonControllers {
    final TextEditingController name;
    final TextEditingController age;
    final TextEditingController job;

    PersonControllers(this.name, this.age, this.job);
}

Then in your widget

final List<PersonControllers> personControllers = List<PersonControllers>();

In your initState

personControllers.add(PersonController(TextEditingController(),TextEditingController(),TextEditingController());

Create a buildCard method:

Widget buildCard(PersonControllers controllers){
    return Card(
        shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(15.0),
        ),
        elevation: 10,
        child: Padding(
            padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
            child: Column(
                children: <Widget>[
                    Text('Person 1'),
                    SizedBox(height: 3.0),
                    _buildNameField(controllers.name),
                    SizedBox(height: 10.0),
                    _buildAgeField(controllers.age),
                    SizedBox(height: 10.0),
                    _buildJobField(controllers.job),
                    SizedBox(height: 10.0),
                ],
            ),
        ),
    );
}

Finally in your build method:

return Scaffold(
    appBar: AppBar(
      title: Text('New Entry'),
    ),
    body: SingleChildScrollView(
      child: Container(
        child: Container(
          color: Colors.white,
          child: Padding(
            padding: const EdgeInsets.all(15.0),
            child: Column(
              children: <Widget>[
                ...personControllers.map((personController) => _buildCard(personController),
                SizedBox(
                  height: 10.0,
                ),
                RaisedButton(
                     child: Text("Add"),
                     onPressed: (){
                         setState((){
                             personControllers.add(PersonController(
                                 TextEditingController(),
                                 TextEditingController(),
                                 TextEditingController()
                            });
                        );
                     }
                 ),
              ],
            ),
          ),
        ),
      ),
    ))

Solution 2:[2]

if you want remove the form also please look at this code may be it is help full for someone

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

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

class _TestState extends State<Test> {
    var nameTECs = <int, TextEditingController>{};
 var mailTECs = <int, TextEditingController>{};
 List<Entry> entries = [];

 var item = <int, Widget>{};

 GlobalKey<FormState> _formKey = GlobalKey<FormState>();

   @override
  void didChangeDependencies() {
   super.didChangeDependencies();
   item.addAll({0: newMethod(context, 0)});
    }

  ondDone() {
  entries.clear();
   print(nameTECs.keys.last);
   for (int i = 0; i <= nameTECs.keys.last; i++) {
  var name = nameTECs[i]?.value.text;
  var mail = mailTECs[i]?.value.text;

  // print(mailTECs[i]?.value.text);
  if (name != null && mail != null) {
    entries.add(Entry(name, mail));
  }

}
print(entries);
for (int a = 0; a < entries.length; a++) {
  
  print(entries[a].name);
  print(entries[a].email);
}
 }

     newMethod(
       BuildContext context,
       int index,
       ) {
    var nameController = TextEditingController();
   var mailController = TextEditingController();
  nameTECs.addAll({index: nameController});
mailTECs.addAll({index: mailController});
return Column(
  children: [
    Text(index.toString()),
   TextFormField(
      controller: nameController,
      validator: (value) {
        return value!.isEmpty ? 'Enter some text' : null;
      },
      textFieldType: TextFieldType.NAME,
    
    ),
  
    TextFormField(
      controller: mailController,
      validator: (value) {},
      // controller: nameCount,
      textFieldType: TextFieldType.NAME,
    
    ),
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        FlatButton(
          onTap: () {
            item.addAll({item.keys.last+1: newMethod(context, item.keys.last + 1)});
            setState(() {});

            // }
          },
       
          child: Text('Add'),
        ),
        FlatButton(
          onTap: () {
            setState(() {
              item.removeWhere((key, value) => key == index);
              nameTECs.removeWhere((key, value) => key == index);
              mailTECs.removeWhere((key, value) => key == index);
            });
          },
        
          child: Text('Remove'),
        ),
      ],
    ),
  ],
);
}

 @override
  Widget build(BuildContext context) {
print('build');
return Scaffold(
  appBar: AppBar(
    title: Text('Test'),
  ),
  body: SingleChildScrollView(
    child: Form(
      key: _formKey,
      child: Column(
        children: [
       

          ListView.builder(
              shrinkWrap: true,
              physics: ScrollPhysics(),
              itemCount: item.length,
              itemBuilder: (context, index) {
                return item.values.elementAt(index);
              }),

          // for (int i = 0; i < widgeta.length; i++) widgeta[i],
          AppButton(
            onTap: () {
              if (_formKey.currentState!.validate()) {
                ondDone();
                // _formKey.currentState!.save();
                setState(() {});
              }
            },
            color: appPrimaryColor,
            child: Text('save'),
          ),
          Center(
            child: Text('Test', textDirection: TextDirection.rtl),
          ),
        ],
         ),
       ),
     ),
   );
   }
   }

    class Entry {
   final String? name;
  final String? email;

    Entry(
   this.name,
  this.email,
  );


  }

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pyth0nGh057
Solution 2 Akbar Masterpadi