How to make a multi column Flutter DataTable widget span the full width?
Asked Answered
D

10

30

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),
                ),
          ),
        ),
      ]))
    );
  }
Duntson answered 17/6, 2019 at 5:12 Comment(3)
what is the parent of your SizedBox.expand ? could you add your build methodPhyto
@Phyto it is a child of SingleChildScrollView, which is a child of a ContainerDuntson
@Eugene I've added the full widget code to the original postDuntson
P
34

You can add the crossAxisAlignment for your Column to strech

crossAxisAlignment: CrossAxisAlignment.stretch
Phyto answered 17/6, 2019 at 14:20 Comment(2)
This makes the column stretch but it doesn't take the DataTable widget to stretch with it. It seems inherently limited in the DataTable itself.Duntson
@Duntson Works fine for meInspiration
R
22

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: []),
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  );
}
Roberts answered 20/12, 2019 at 23:39 Comment(2)
Thanks! Works like charm! I give you thumbs. :)Elliotelliott
In my case, I had to change constraints.minWidth to constraints.maxWidth and remove the ScrollViews to do the trickSettlings
S
17

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.

Squally answered 8/6, 2020 at 13:39 Comment(2)
The second one is super great, works with ScrollBar too. Just released my app without testing on tablets. get i was too excited. Now updates are on the way!Seventieth
"otherwise this question would be irrelevant..." not necessarily. I need the horizontal scrolling to make the UI responsive, taking up the full width when the view area is wide and scrollable when it is narrow.Rubefaction
L
13

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

Container(
    width: double.infinity,
    child: DataTable(
      columns: _columns,
      rows: _rows,
    ));
Lorou answered 24/7, 2021 at 4:44 Comment(0)
T
8

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),
    ];
  }
}
Tiler answered 28/2, 2020 at 14:25 Comment(0)
D
5

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

SizedBox(
  width: double.infinity,
  child: DataTable()
)
Distort answered 27/1, 2022 at 11:19 Comment(0)
C
4

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.

Charmainecharmane answered 23/2, 2022 at 18:48 Comment(0)
F
1

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.

Fife answered 24/7, 2021 at 4:49 Comment(0)
V
0
    SingleChildScrollView(
        child: Card(
          child: SizedBox(
            width: double.infinity,
            child: DataTable(columns:_columns, rows:_rows),
          ),
        ),
      ),
Vickievicksburg answered 15/12, 2022 at 16:6 Comment(2)
You might want to add some more details, code-only answer are not very usefulPolyphagia
The code said it all. I feel like there's no need to explain it.Vickievicksburg
C
0
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

Couloir answered 17/1 at 19:0 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Penick

© 2022 - 2024 — McMap. All rights reserved.