user1961
user1961

Reputation: 1500

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),
                ),
          ),
        ),
      ]))
    );
  }

Upvotes: 32

Views: 38679

Answers (10)

Prasenjit Das
Prasenjit Das

Reputation: 1

SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: DataTable(
        columnSpacing: MediaQuery.of(context).size.width * 0.2,
        columns: const [
          DataColumn(label: Text('Product Name')),
          DataColumn(label: Text('Product Price')),
          DataColumn(label: Text('Product Type')),
          DataColumn(label: Text('Product Category')),
          DataColumn(label: Text('Product Description')),
        ],
        rows: productsList.map((product) {
          return DataRow(cells: [
            DataCell(Text(product.productName)),
            DataCell(Text(product.price)),
            DataCell(Text(product.typeOfProduct)),
            DataCell(Text(product.categoryOfProduct)),
            DataCell(Text(product.description)),
          ]);
        }).toList(),
      ),
    ),

simple solution is to use columnSpacing and use this formula (width of screen / number of columns === width of the screen * (1/number of columns)) that is columnSpacing: MediaQuery.of(context).size.width * 0.2 because in my case column count is 5 so (screen width * (1/5 which is 0.2)).

hope this help Don't forget to wrap the DataTable with SingleChildScrollView

Upvotes: 0

Paulo
Paulo

Reputation: 684

    SingleChildScrollView(
        child: Card(
          child: SizedBox(
            width: double.infinity,
            child: DataTable(columns:_columns, rows:_rows),
          ),
        ),
      ),

Upvotes: 0

MBK
MBK

Reputation: 3354

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.

Upvotes: 4

Shakthi Hettiarachchi
Shakthi Hettiarachchi

Reputation: 489

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

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

Upvotes: 6

DrGeneral
DrGeneral

Reputation: 2132

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.

Upvotes: 1

Parth Gupta
Parth Gupta

Reputation: 151

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

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

Upvotes: 14

Raiden Core
Raiden Core

Reputation: 1605

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.

Upvotes: 19

Ramesh kumar
Ramesh kumar

Reputation: 975

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),
    ];
  }
}

Upvotes: 8

Ben Konyi
Ben Konyi

Reputation: 3219

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: []),
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

Upvotes: 22

diegoveloper
diegoveloper

Reputation: 103421

You can add the crossAxisAlignment for your Column to strech

crossAxisAlignment: CrossAxisAlignment.stretch

Upvotes: 35

Related Questions