flutter create dynamic TextField when button click

11,033

Solution 1

import 'package:flutter/material.dart';

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text('Add entries'),
          onPressed: () async {
            List<PersonEntry> persons = await Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => SOF(),
              ),
            );
            if (persons != null) persons.forEach(print);
          },
        ),
      ),
    );
  }
}

class SOF extends StatefulWidget {
  @override
  _SOFState createState() => _SOFState();
}

class _SOFState extends State<SOF> {
  var nameTECs = <TextEditingController>[];
  var ageTECs = <TextEditingController>[];
  var jobTECs = <TextEditingController>[];
  var cards = <Card>[];

  Card createCard() {
    var nameController = TextEditingController();
    var ageController = TextEditingController();
    var jobController = TextEditingController();
    nameTECs.add(nameController);
    ageTECs.add(ageController);
    jobTECs.add(jobController);
    return Card(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Text('Person ${cards.length + 1}'),
          TextField(
              controller: nameController,
              decoration: InputDecoration(labelText: 'Full Name')),
          TextField(
              controller: ageController,
              decoration: InputDecoration(labelText: 'Age')),
          TextField(
              controller: jobController,
              decoration: InputDecoration(labelText: 'Study/ job')),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    cards.add(createCard());
  }

  _onDone() {
    List<PersonEntry> entries = [];
    for (int i = 0; i < cards.length; i++) {
      var name = nameTECs[i].text;
      var age = ageTECs[i].text;
      var job = jobTECs[i].text;
      entries.add(PersonEntry(name, age, job));
    }
    Navigator.pop(context, entries);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: <Widget>[
          Expanded(
            child: ListView.builder(
              itemCount: cards.length,
              itemBuilder: (BuildContext context, int index) {
                return cards[index];
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: RaisedButton(
              child: Text('add new'),
              onPressed: () => setState(() => cards.add(createCard())),
            ),
          )
        ],
      ),
      floatingActionButton:
          FloatingActionButton(child: Icon(Icons.done), onPressed: _onDone),
    );
  }
}

class PersonEntry {
  final String name;
  final String age;
  final String studyJob;

  PersonEntry(this.name, this.age, this.studyJob);
  @override
  String toString() {
    return 'Person: name= $name, age= $age, study job= $studyJob';
  }
}

Solution 2

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()
                            });
                        );
                     }
                 ),
              ],
            ),
          ),
        ),
      ),
    ))
Share:
11,033

Related videos on Youtube

Ashique bzq
Author by

Ashique bzq

// Just commented the about me section !

Updated on October 19, 2022

Comments

  • Ashique bzq
    Ashique bzq over 1 year

    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
                  ],
                ),
              ),
            ),
          ),
        ))
    
  • K.Amanov
    K.Amanov over 2 years
    don't forget to dispose controllers ;-)
  • isso kd
    isso kd over 2 years
    Hello ur code is perfect.. But can you help me.. I want all what u code but instead of 3 textfield i want 1 textfield and an image picker. And i want this image to be shown after capturing it immediatly so that an camera button if image = null else show image instead of camera button. What do u suggest me?