Reputation: 550
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
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
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
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
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