Sarah Pöhler
Sarah Pöhler

Reputation: 550

How to add dynamically items to the table in flutter?

Do someone know how to add dynamically more rows into the DataTable in Flutter. As you can see my code is very 'hardcoded' [line: 11-31].
There should be a way to get rid of writing more and more DataRows.

Code:

class DataTableWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DataTable(
      columns: [
        DataColumn(label: Text('Patch')),
        DataColumn(label: Text('Version')),
        DataColumn(label: Text('Ready')),
      ],
      rows: <DataRow>[
        DataRow(
          cells: <DataCell>[
            DataCell(Text('AAAAAA')),
            DataCell(Text('1')),
            DataCell(Text('Yes')),
          ],
        ),
        DataRow(
          cells: <DataCell>[
            DataCell(Text('BBBBBB')),
            DataCell(Text('2')),
            DataCell(Text('No')),
          ],
        ),
        DataRow(
          cells: <DataCell>[
            DataCell(Text('CCCCCC')),
            DataCell(Text('3')),
            DataCell(Text('Yes')),
          ],
        ),
      ],
    );
  }
}

Upvotes: 17

Views: 44141

Answers (4)

Hinata
Hinata

Reputation: 21

You can add like this, In a column, I've added an add item button, which will add another row to the table every time you click on it. Add the below code in a column.

MaterialButton(
        onPressed: () {
        setState(() {
          details.add({
            "Name": "",
            "Maths": "",
            "Science": "",
            "Total": "",
          });
        });
      },
        color: Colors.blue,
        child: const Text("Add Item"),
      ),
      const SizedBox(height: 10,),
      SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: LayoutBuilder(
          builder: (context, constraints) {
            final rowHeight = (constraints.maxHeight - 40) / details.length;
            return DataTable(
                dataRowMaxHeight: rowHeight,
                dataRowMinHeight: 35,
                headingRowHeight: 30,
                headingRowColor:  MaterialStatePropertyAll(Colors.blue[50]),
                columns: const [
                  DataColumn(label: Text('Name')),
                  DataColumn(label: Text('Maths')),
                  DataColumn(label: Text('Science')),
                  DataColumn(label: Text('Total')),
                  DataColumn(label: Text('')),
                ],
                rows: List.generate(details.length, (index) {
                  TextEditingController nameController = TextEditingController();
                  TextEditingController mathsController = TextEditingController();
                  TextEditingController scienceController = TextEditingController();
                  TextEditingController totalController = TextEditingController();
                  nameController.text = details[index]['Name']!;
                  mathsController.text = details[index]['Maths']!;
                  scienceController.text = details[index]['Science']!;
                  nameController.selection = TextSelection.fromPosition(TextPosition(offset: nameController.text.length));
                  mathsController.selection = TextSelection.fromPosition(TextPosition(offset: mathsController.text.length));
                  scienceController.selection = TextSelection.fromPosition(TextPosition(offset: scienceController.text.length));
                  return DataRow(
                      cells: [
                        DataCell(
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Container(
                                width: 200,
                                height: 50,
                                color: Colors.blue[50],
                                child: const TextField(
                                  // expands: true,
                                  maxLines: null,
                                  decoration: InputDecoration(
                                      border: InputBorder.none
                                  ),
                                ),
                              ),
                            )
                        ),
                        DataCell(
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Container(
                                color: Colors.blue[50],
                                child: SizedBox(
                                  height: 30,
                                  width: 50,
                                  child: TextField(
                                    controller: mathsController,
                                    style: const TextStyle(fontSize: 12),
                                    decoration: const InputDecoration(
                                        border: InputBorder.none,
                                        contentPadding: EdgeInsets.only(bottom: 20, left: 3, right: 3)
                                    ),
                                    onChanged: (value) {
                                      try{
                                        double maths = double.parse(value);
                                        setState(() {
                                          details[index]['Maths'] = maths.toString();
                                          double science = double.tryParse(details[index]['Science'] ?? "")?? 0;
                                          details[index]['Total'] = (maths + science).toString();
                                        });
                                      }catch(e){
                                        print("Invalid input for Maths: $value");
                                      }
                                    },
                                  ),
                                ),
                              ),
                            )
                        ),
                        DataCell(
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Container(
                                color: Colors.blue[50],
                                child: SizedBox(
                                  height: 30,
                                  width: 50,
                                  child: TextField(
                                    controller: scienceController,
                                    style: const TextStyle(fontSize: 12),
                                    decoration: const InputDecoration(
                                        border: InputBorder.none,
                                        contentPadding: EdgeInsets.only(bottom: 20, left: 3, right: 3)
                                    ),
                                    onChanged: (value) {
                                      try {
                                        double science = double.parse(value);
                                        setState(() {
                                          details[index]["Science"] = science.toString();
                                          double maths = double.tryParse(details[index]['Maths'] ?? '') ?? 0;
                                          details[index]['Total'] = (maths + science).toString();
                                        });
                                      } catch (e) {
                                        print("Invalid input for Science: $value");
                                      }
                                    },
                                  ),
                                ),
                              ),
                            )
                        ),
                        DataCell(
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Container(
                                color: Colors.blue[50],
                                child: SizedBox(
                                  height: 30,
                                  width: 50,
                                  child: TextField(
                                    readOnly: true,
                                    controller: TextEditingController(text: details[index]['Total']),
                                    style: const TextStyle(fontSize: 12),
                                    decoration: const InputDecoration(
                                        border: InputBorder.none,
                                        contentPadding: EdgeInsets.only(bottom: 20, left: 3, right: 3)
                                    ),
                                    onChanged: (value) {
                                      details[index]["Total"] = value;
                                      totalController.text = value;
                                    },
                                  ),
                                ),
                              ),
                            )
                        ),
                        DataCell(
                            ElevatedButton(
                                onPressed: () {

                                }, child: const Text("Edit"))
                        )
                      ]
                  );
                })
            );
          },
        ),
      ),

This is the list where I'm rendering data from.

List<Map<String, String>> details = [{
"Name": "Rahul",
"Maths": "80",
"Science": "70",
"Total": "150",},{
"Name": "Babu",
"Maths": "50",
"Science": "50",
"Total": "100",},];

Upvotes: 0

srking
srking

Reputation: 4732

The underlying data structure, probably a list, is what you want to worry about changing. UI elements like the table just observe. The setState() function notifies the UI to take a fresh look after you've updated your list.

Here's a working example hacked from a flutter doc example. Click the button to add rows.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Add Rows',
      home: MyHomePage(title: 'Add Rows'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<DataRow> _rowList = [
    DataRow(cells: <DataCell>[
      DataCell(Text('AAAAAA')),
      DataCell(Text('1')),
      DataCell(Text('Yes')),
    ]),
  ];

  void _addRow() {
    // Built in Flutter Method.
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below.
      _rowList.add(DataRow(cells: <DataCell>[
        DataCell(Text('BBBBBB')),
        DataCell(Text('2')),
        DataCell(Text('No')),
      ]));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: DataTable(columns: [
          DataColumn(label: Text('Patch')),
          DataColumn(label: Text('Version')),
          DataColumn(label: Text('Ready')),
        ], rows: _rowList),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _addRow,
        label: Text('Add Row'),
        backgroundColor: Colors.green,
      ),
    );
  }
}

This builds for me with

flutter build web

then run with

flutter run -d chrome

Upvotes: 5

dshukertjr
dshukertjr

Reputation: 18612

You could do something like this:**

class DataTableWidget extends StatelessWidget {

      List results=[] ;
       intState((){
         super.iniState();
            this.getSale();
 })
Future<String> getData () async {

var response = await http.get(
  "$saleUrl/?format=json",

);
setState(() {
  var dataConvertedToJson = 
json.decode(utf8.decode(response.bodyBytes));
  results = dataConvertedToJson['results'];
});
 print('${results.length}');
  return "successful";
 }
  DataRow _getDataRow(result) {
    return DataRow(
      cells: <DataCell>[
        DataCell(Text(data["text1"])),
        DataCell(Text(data["text2"])),
        DataCell(Text(data["text3"])),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return DataTable(
      columns: [
        DataColumn(label: Text('Patch')),
        DataColumn(label: Text('Version')),
        DataColumn(label: Text('Ready')),
      ],
      rows: List.generate(
          results.length, (index) => _getDataRow(results[index])),
    );
  }
}

Upvotes: 14

Guy Luz
Guy Luz

Reputation: 4009

You can use

listOfColumns.map(((element) => DataRow(...))).toList()

This is your code using this method.

class DataTableWidget extends StatelessWidget {
  final List<Map<String, String>> listOfColumns = [
    {"Name": "AAAAAA", "Number": "1", "State": "Yes"},
    {"Name": "BBBBBB", "Number": "2", "State": "no"},
    {"Name": "CCCCCC", "Number": "3", "State": "Yes"}
  ];
//  DataTableWidget(this.listOfColumns);     // Getting the data from outside, on initialization
  @override
  Widget build(BuildContext context) {
    return DataTable(
      columns: [
        DataColumn(label: Text('Patch')),
        DataColumn(label: Text('Version')),
        DataColumn(label: Text('Ready')),
      ],
      rows:
          listOfColumns // Loops through dataColumnText, each iteration assigning the value to element
              .map(
                ((element) => DataRow(
                      cells: <DataCell>[
                        DataCell(Text(element["Name"])), //Extracting from Map element the value
                        DataCell(Text(element["Number"])),
                        DataCell(Text(element["State"])),
                      ],
                    )),
              )
              .toList(),
    );
  }
}

Upvotes: 34

Related Questions