Flutter DataTable Widget

Flutter’s DataTable widget displays structured data in rows and columns. It is suitable for a small set of records that users need to scan, compare, select, or sort.

Each table is built from DataColumn, DataRow, and DataCell objects. Column labels describe the fields, while every row supplies one cell for each column.

Performance note: DataTable measures its columns before laying out the table, so it can be expensive for a large number of rows. For larger data sets, consider pagination or a lazily built table interface instead of placing every record in one DataTable.

Flutter DataTable Syntax

Following is the syntax of a DataTable.

</>
Copy
DataTable(
  columns: [
	DataColumn(label: ),
	DataColumn(label: ),
	...
	DataColumn(label: )),
  ],
  rows: [
	DataRow(cells: [
	  DataCell( ),
	  DataCell( ),
	  ...
	  DataCell( ),
	]),
	DataRow(cells: [
	  DataCell( ),
	  DataCell( ),
	  ...
	  DataCell( ),
	]),
	...
	DataRow(cells: [
	  DataCell( ),
	  DataCell( ),
	  ...
	  DataCell( ),
	]),
  ],
),

The columns property receives a list of DataColumn objects, and the rows property receives a list of DataRow objects. Each DataRow contains a cells list made up of DataCell objects.

The number of cells in every row must match the number of columns. For example, a table with three DataColumn objects requires exactly three DataCell objects in each row.

The label property of DataColumn accepts a widget. A DataCell also contains a widget, so a table cell can display Text, Icon, Image, a button, or another suitable widget.

Flutter DataTable Example with Three Columns

In the following example, we define a simple DataTable with three column labels and three rows.

main.dart

</>
Copy
import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: Text('Flutter Tutorial - TutorialKart'),
          ),
          body: ListView(children: <Widget>[
            Center(
                child: Text(
              'Students',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            )),
            DataTable(
              columns: [
                DataColumn(label: Text('RollNo')),
                DataColumn(label: Text('Name')),
                DataColumn(label: Text('Class')),
              ],
              rows: [
                DataRow(cells: [
                  DataCell(Text('1')),
                  DataCell(Text('Arya')),
                  DataCell(Text('6')),
                ]),
                DataRow(cells: [
                  DataCell(Text('12')),
                  DataCell(Text('John')),
                  DataCell(Text('9')),
                ]),
                DataRow(cells: [
                  DataCell(Text('42')),
                  DataCell(Text('Tony')),
                  DataCell(Text('8')),
                ]),
              ],
            ),
          ])),
    );
  }
}

The example places the table inside a ListView, allowing the page to scroll vertically when the available screen height is limited. The first row of the table displays the column labels, followed by the three student records.

When you run this Flutter application, you would get the DataTable displayed in UI as shown below.

Flutter DataTable

Create Flutter DataTable Rows from a List

Application data is usually stored in a list rather than written as individual DataRow objects. Use map() to convert each item into a row.

</>
Copy
class Student {
  const Student({
    required this.rollNumber,
    required this.name,
    required this.studentClass,
  });

  final int rollNumber;
  final String name;
  final int studentClass;
}

const students = [
  Student(rollNumber: 1, name: 'Arya', studentClass: 6),
  Student(rollNumber: 12, name: 'John', studentClass: 9),
  Student(rollNumber: 42, name: 'Tony', studentClass: 8),
];

DataTable(
  columns: const [
    DataColumn(label: Text('Roll No.')),
    DataColumn(label: Text('Name')),
    DataColumn(label: Text('Class')),
  ],
  rows: students.map((student) {
    return DataRow(
      cells: [
        DataCell(Text('${student.rollNumber}')),
        DataCell(Text(student.name)),
        DataCell(Text('${student.studentClass}')),
      ],
    );
  }).toList(),
)

This approach keeps the table synchronized with the underlying data. When the list changes and the widget rebuilds, Flutter generates the rows from the current list values.

Sort a Flutter DataTable Column

A sortable column uses the onSort callback of DataColumn. Store the active column index and sort direction in state, sort the data list, and call setState() to rebuild the table.

</>
Copy
int? sortColumnIndex;
bool sortAscending = true;

void sortByName(int columnIndex, bool ascending) {
  students.sort((first, second) {
    final comparison = first.name.compareTo(second.name);
    return ascending ? comparison : -comparison;
  });

  setState(() {
    sortColumnIndex = columnIndex;
    sortAscending = ascending;
  });
}

DataTable(
  sortColumnIndex: sortColumnIndex,
  sortAscending: sortAscending,
  columns: [
    const DataColumn(label: Text('Roll No.')),
    DataColumn(
      label: const Text('Name'),
      onSort: sortByName,
    ),
    const DataColumn(label: Text('Class')),
  ],
  rows: students.map((student) {
    return DataRow(
      cells: [
        DataCell(Text('${student.rollNumber}')),
        DataCell(Text(student.name)),
        DataCell(Text('${student.studentClass}')),
      ],
    );
  }).toList(),
)

Flutter displays a sort indicator on the column referenced by sortColumnIndex. The sortAscending</code value controls the direction of that indicator.</p> <!-- /wp:paragraph --> <!-- wp:heading {"level":3} --> <h3>Select Rows in a Flutter DataTable</h3> <!-- /wp:heading --> <!-- wp:paragraph --> <p>Set <code>selected and onSelectChanged on a DataRow to make it selectable. Flutter displays a checkbox column when at least one row provides an onSelectChanged callback.

</>
Copy
DataRow(
  selected: selectedStudentIds.contains(student.rollNumber),
  onSelectChanged: (selected) {
    setState(() {
      if (selected == true) {
        selectedStudentIds.add(student.rollNumber);
      } else {
        selectedStudentIds.remove(student.rollNumber);
      }
    });
  },
  cells: [
    DataCell(Text('${student.rollNumber}')),
    DataCell(Text(student.name)),
    DataCell(Text('${student.studentClass}')),
  ],
)

Use a stable identifier, such as a record ID, to track selected rows. This prevents selection state from becoming attached to the wrong item after sorting or filtering.

Handle Taps and Edits in Flutter DataCell

A DataCell can respond to taps, double taps, long presses, and tap-down events. The following cell opens an editing function when the displayed name is tapped.

</>
Copy
DataCell(
  Text(student.name),
  onTap: () {
    editStudent(student);
  },
)

For inline editing, place an input widget such as TextField inside the cell and keep its value in application state. For a simpler table, opening a dialog or another screen is often easier to manage.

Make Flutter DataTable Horizontally Scrollable

A table with many columns can be wider than a phone screen. Wrap it in a horizontal SingleChildScrollView so users can reach columns that do not fit within the viewport.

</>
Copy
SingleChildScrollView(
  scrollDirection: Axis.horizontal,
  child: DataTable(
    columns: const [
      DataColumn(label: Text('Roll No.')),
      DataColumn(label: Text('Name')),
      DataColumn(label: Text('Class')),
      DataColumn(label: Text('Section')),
      DataColumn(label: Text('Attendance')),
    ],
    rows: rows,
  ),
)

When the complete page also needs vertical scrolling, use separate scrolling widgets carefully. Avoid giving two scroll views the same scrolling direction unless their constraints and interaction are intentional.

Customize Flutter DataTable Spacing and Colors

DataTable provides properties for heading styles, row colors, column spacing, margins, row heights, dividers, and checkbox visibility.

</>
Copy
DataTable(
  headingRowColor: WidgetStatePropertyAll(
    Colors.blueGrey.shade50,
  ),
  dataRowColor: WidgetStateProperty.resolveWith((states) {
    if (states.contains(WidgetState.selected)) {
      return Colors.blue.shade50;
    }
    return null;
  }),
  headingTextStyle: const TextStyle(
    fontWeight: FontWeight.bold,
  ),
  columnSpacing: 32,
  horizontalMargin: 20,
  dividerThickness: 1,
  columns: const [
    DataColumn(label: Text('Roll No.')),
    DataColumn(label: Text('Name')),
    DataColumn(label: Text('Class')),
  ],
  rows: rows,
)

Use state-aware colors when a row needs a different appearance while selected, hovered, focused, or pressed. Keep sufficient contrast between text and the chosen background.

Align Numeric Flutter DataTable Columns

Set numeric: true on a DataColumn that contains numbers. This gives the column numeric alignment behavior and communicates that its values should be compared as numbers.

</>
Copy
const DataColumn(
  label: Text('Score'),
  numeric: true,
)

Store sortable numeric values as numbers rather than formatted strings. This avoids incorrect text ordering, such as placing 100 before 20.

Choose DataTable or PaginatedDataTable in Flutter

Use DataTable when the complete data set is small enough to display and build at once. Use PaginatedDataTable when records should be divided into pages and supplied through a DataTableSource.

  • Use DataTable for a small, directly available list of rows.
  • Use PaginatedDataTable when users need page controls and selectable rows across a larger collection.
  • Use a lazily built or specialized data-grid solution when the table contains many rows, many columns, complex editing, frozen sections, or extensive virtualization requirements.

Common Flutter DataTable Errors

DataRow Has the Wrong Number of DataCell Widgets

Every row must contain the same number of cells as the table has columns. Check the generated row list if the table fails with an assertion related to the cell count.

Flutter DataTable Overflows the Screen Width

Wrap the table in a horizontal SingleChildScrollView, reduce unnecessary spacing, shorten labels, or reconsider how many columns need to appear on a narrow screen.

Sorted Data Does Not Update the DataTable

Sort the list used to create the rows and call setState() after updating the sort column and direction. Sorting a separate list that is not used by the widget will not change the displayed order.

Flutter DataTable Is Slow with Many Rows

A standard DataTable builds and lays out all supplied rows. Reduce the number of displayed records, paginate the data, or use a table implementation designed to build visible rows lazily.

Flutter DataTable Editorial QA Checklist

  • Confirm that every DataRow has exactly one DataCell for each DataColumn.
  • Check that numeric columns use numeric values for sorting and numeric: true where appropriate.
  • Test the table at narrow screen widths and add horizontal scrolling when columns overflow.
  • Verify that row selection remains attached to stable record IDs after sorting or filtering.
  • Review row count and layout cost before using DataTable for a large data set.

Flutter DataTable Summary

A Flutter DataTable is created with a list of DataColumn objects and a list of DataRow objects containing matching DataCell entries. It supports sorting, selection, interactive cells, numeric columns, and visual customization. For wider tables, add horizontal scrolling; for substantially larger data sets, use pagination or a lazily built alternative. Continue with the other examples in this Flutter Tutorial.