'How to make a multi column Flutter DataTable widget span the full width?

I have a 2 column flutter DataTable and the lines don't span the screen width leaving lots of white space. I found this issue

https://github.com/flutter/flutter/issues/12775

That recommended wrapping the DataTable in a SizedBox.expand widget but that does not work produces RenderBox was not laid out:

SizedBox.expand(
                    child: DataTable(columns:_columns, rows:_rows),
            ),

enter image description here

Full widget

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body:
      SingleChildScrollView(
      child: Column(
        children: [Container(Text('My Text')),
        Container(
          alignment: Alignment.topLeft,
          child: SingleChildScrollView(scrollDirection: Axis.horizontal,
            child: SizedBox.expand(
                        child: DataTable(columns:_columns, rows:_rows),
                ),
          ),
        ),
      ]))
    );
  }


Solution 1:[1]

You can add the crossAxisAlignment for your Column to strech

crossAxisAlignment: CrossAxisAlignment.stretch

Solution 2:[2]

SizedBox.expand results in the DataTable taking an infinite height which the SingleChildScrollView won't like. Since you only want to span the width of the parent, you can use a LayoutBuilder to get the size of the parent you care about and then wrap the DataTable in a ConstrainedBox.

Widget build(BuildContext context) {
  return Scaffold(
    body: LayoutBuilder(
      builder: (context, constraints) => SingleChildScrollView(
        child: Column(
          children: [
            const Text('My Text'),
            Container(
              alignment: Alignment.topLeft,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: ConstrainedBox(
                  constraints: BoxConstraints(minWidth: constraints.minWidth),
                  child: DataTable(columns: [], rows: []),
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

Solution 3:[3]

This is an issue, incompleteness, in an otherwise beautiful Widget which is the DataTable, I faced this issue in a production code, this solution worked on more than half of the lab devices:

ConstrainedBox(
        constraints: BoxConstraints.expand( 
                  width: MediaQuery.of(context).size.width
        ),
child: DataTable( // columns and rows.),)

But you know what suprisingly worked on %100 of the devices ? this:

Row( // a dirty trick to make the DataTable fit width
      children: <Widget>[ 
        Expanded(
          child: SingleChildScrollView(
          scrollDirection: Axis.vertical,
          child: DataTable(...) ...]//row children

Note: The Row has only one child Expanded which in turn enclose a SingleChildScrollView which in turn enclose the DataTable.

Note that this way you cant use SingleChileScrollView with scrollDirection: Axis.horizontal, in case you need it, but you dont otherwise this question would be irrelevant to your use case.

In case someone of the Flutter team reads this, please enrich the DataTable Widget, it will make flutter competitive and powerful, flutter may eclipse androids own native API if done right.

Solution 4:[4]

For DataTable widget this code has worked for me regarding dataTable width as match parent to device-width,

Code snippet:

ConstrainedBox(
constraints: 
BoxConstraints.expand(
   width: MediaQuery.of(context).size.width
),
child: 
DataTable(
    // inside dataTable widget you must have columns and rows.),)

and you can remove space between columns by using attribute like

 columnSpacing: 0,

Note:

using ConstrainedBox widget solves your issue,

constraints: BoxConstraints.expand(width: MediaQuery.of(context).size.width),

Complete Code :

Note: In this sample code, I covered sorting and editing DataTable widget concepts.

In Lib Folder you must have this class

  1. main.dart
  2. DataTableDemo.dart
  3. customer.dart

main.dart class code

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'DataTableDemo.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DataTableDemo(),
    );
  }
}

DataTableDemo.dart class code

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'customer.dart';

class DataTableDemo extends StatefulWidget {
  DataTableDemo() : super();
  final String title = "Data Table";

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

class DataTableDemoState extends State<DataTableDemo> {
  List<customer> users;
  List<customer> selectedUsers;
  bool sort;
  TextEditingController _controller;
  int iSortColumnIndex = 0;
  int iContact;

  @override
  void initState() {
    sort = false;
    selectedUsers = [];
    users = customer.getUsers();


    _controller = new TextEditingController();

    super.initState();
  }

  onSortColum(int columnIndex, bool ascending) {
    if (columnIndex == 0) {
      if (ascending) {
        users.sort((a, b) => a.firstName.compareTo(b.firstName));
      } else {
        users.sort((a, b) => b.firstName.compareTo(a.firstName));
      }
    }
  }

  onSelectedRow(bool selected, customer user) async {
    setState(() {
      if (selected) {
        selectedUsers.add(user);
      } else {
        selectedUsers.remove(user);
      }
    });
  }

  deleteSelected() async {
    setState(() {
      if (selectedUsers.isNotEmpty) {
        List<customer> temp = [];
        temp.addAll(selectedUsers);
        for (customer user in temp) {
          users.remove(user);
          selectedUsers.remove(user);
        }
      }
    });
  }

  SingleChildScrollView dataBody() {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: ConstrainedBox(
        constraints: BoxConstraints.expand(width: MediaQuery.of(context).size.width),
        child: DataTable(
          sortAscending: sort,
          sortColumnIndex: iSortColumnIndex,
          columns: [
            DataColumn(
                label: Text("FIRST NAME"),
                numeric: false,
                tooltip: "This is First Name",
                onSort: (columnIndex, ascending) {
                  setState(() {
                    sort = !sort;
                  });
                  onSortColum(columnIndex, ascending);
                }),
            DataColumn(
              label: Text("LAST NAME"),
              numeric: false,
              tooltip: "This is Last Name",
            ),
            DataColumn(label: Text("CONTACT NO"), numeric: false, tooltip: "This is Contact No")
          ],
          columnSpacing: 2,
          rows: users
              .map(
                (user) => DataRow(
                    selected: selectedUsers.contains(user),
                    onSelectChanged: (b) {
                      print("Onselect");
                      onSelectedRow(b, user);
                    },
                    cells: [
                      DataCell(
                        Text(user.firstName),
                        onTap: () {
                          print('Selected ${user.firstName}');
                        },
                      ),
                      DataCell(
                        Text(user.lastName),
                      ),
                      DataCell(Text("${user.iContactNo}"),
                          showEditIcon: true, onTap: () => showEditDialog(user))
                    ]),
              )
              .toList(),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.stretch,
//          verticalDirection: VerticalDirection.down,
          children: <Widget>[
            Expanded(
              child: Container(
                child: dataBody(),
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Padding(
                  padding: EdgeInsets.all(20.0),
                  child: OutlineButton(
                    child: Text('SELECTED ${selectedUsers.length}'),
                    onPressed: () {},
                  ),
                ),
                Padding(
                  padding: EdgeInsets.all(20.0),
                  child: OutlineButton(
                    child: Text('DELETE SELECTED'),
                    onPressed: selectedUsers.isEmpty ? null : () => deleteSelected(),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  void showEditDialog(customer user) {
    String sPreviousText = user.iContactNo.toString();
    String sCurrentText;
    _controller.text = sPreviousText;

    showDialog(
      barrierDismissible: false,
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: new Text("Edit Contact No"),
          content: new TextFormField(
            controller: _controller,
            keyboardType: TextInputType.number,
            decoration: InputDecoration(labelText: 'Enter an Contact No'),
            onChanged: (input) {
              if (input.length > 0) {
                sCurrentText = input;
                iContact = int.parse(input);
              }
            },
          ),
          actions: <Widget>[
            new FlatButton(
              child: new Text("Save"),
              onPressed: () {
                setState(() {
                  if (sCurrentText != null && sCurrentText.length > 0) user.iContactNo = iContact;
                });
                Navigator.of(context).pop();
              },
            ),
            new FlatButton(
              child: new Text("Cancel"),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }
}

customer.dart class code

class customer {
  String firstName;
  String lastName;
  int iContactNo;

  customer({this.firstName, this.lastName,this.iContactNo});

  static List<customer> getUsers() {
    return <customer>[
      customer(firstName: "Aaryan", lastName: "Shah",iContactNo: 123456897),
      customer(firstName: "Ben", lastName: "John",iContactNo: 78879546),
      customer(firstName: "Carrie", lastName: "Brown",iContactNo: 7895687),
      customer(firstName: "Deep", lastName: "Sen",iContactNo: 123564),
      customer(firstName: "Emily", lastName: "Jane", iContactNo: 5454698756),
    ];
  }
}

Solution 5:[5]

Set your datatable in Container and make container's width as double.infinity

Container(
    width: double.infinity,
    child: DataTable(
      columns: _columns,
      rows: _rows,
    ));

Solution 6:[6]

Just wrap the data table with a container having fixed width defined and everything should work.

Even when you need multiple tables in one screen this worked well for me as of flutter 2.2.3.

final screenWidth = MediaQuery.of(context).size.width;
Scaffold(
  body: SingleChildScrollView(child:Container(
    child: Column(
      children: [
        Container(
            width: screenWidth, // <- important for full screen width
            padding: EdgeInsets.fromLTRB(0, 2, 0, 2),
            child: buildFirstTable() // returns a datatable
        ),
        Container(
            width: screenWidth, // <- this is important
            padding: EdgeInsets.fromLTRB(0, 2, 0, 2),
            child: buildSecondTable() // returns a datatable
        )
    ])
  ))
)

This also works for single table just wrap with container with desired width.

Solution 7:[7]

Simple Answer:

Wrap your datatable with a Container() with width: double.infinity().

    Container(
            width: double.infinity,
            child: DataTable( 
    
            ..
            .

My Prefered Way

You can use DataTable 2 Package at pub.dev
https://pub.dev/packages/data_table_2

This package will give you the DataTable2() widget which will expand to the available space by default. Also you get more options like ColumnSize etc.

Solution 8:[8]

just wrap your DataTable with Sizedbox and give width to double.infinity.

SizedBox(
  width: double.infinity,
  child: DataTable()
)

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 diegoveloper
Solution 2 Ben Konyi
Solution 3
Solution 4
Solution 5 Parth Gupta
Solution 6 DrGeneral
Solution 7
Solution 8 Shakthi Hettiarachchi