如何使多列Flutter DataTable小部件跨越整个宽度?

29

我有一个2列的flutter数据表格,但线条不横跨屏幕宽度,留下了很多空白。我找到了这个问题:

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

建议将DataTable包装在SizedBox.expand widget中,但那样做会产生RenderBox was not laid out:错误。

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

这里输入图片描述

完整小部件

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

你的SizedBox.expand的父级是什么?你能添加你的build方法吗? - diegoveloper
@diegoveloper 它是SingleChildScrollView的子级,而SingleChildScrollView又是Container的子级。 - user1961
@Eugene 我已经在原始帖子中添加了完整的小部件代码。 - user1961
9个回答

32
你可以为你的 Column 添加 crossAxisAlignment 来拉伸。
crossAxisAlignment: CrossAxisAlignment.stretch

2
这会使列拉伸,但它不会使 DataTable 小部件随之拉伸。这似乎在 DataTable 本身上有固有的限制。 - user1961
@user1961 对我来说运行良好。 - West

18

SizedBox.expand 会导致 DataTable 高度无限大,而这是 SingleChildScrollView 所不希望的。由于您只想跨越父级的宽度,因此可以使用 LayoutBuilder 获取您关心的父级大小,然后在 ConstrainedBox 中包装 DataTable

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

谢谢!运行得很好!我给你竖起大拇指。 :) - Csaba Gergely
2
在我的情况下,我不得不将constraints.minWidth更改为constraints.maxWidth,并删除ScrollViews来解决问题。 - pitazzo

16

这是一个问题,即DataTable这个原本漂亮的小部件存在不完整性, 我在生产代码中遇到了这个问题,在实验室设备的一半以上工作正常:

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

但是你知道令人惊讶的是,在所有设备上都百分之百可以运行的是这个:

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

注意:该行(Row)只有一个子项Expanded,而Expanded又包含了一个SingleChildScrollView,该SingleChildScrollView又包含了DataTable。

请注意,通过这种方式,您无法使用 scrollDirection: Axis.horizontal 的SingleChileScrollView,除非您需要它,否则此问题对您的用例不相关。

如果Flutter团队中有人阅读到这篇文章,请丰富DataTable小部件,这将使Flutter更具竞争力和强大,如果做得正确,Flutter可能会超越Android自己的本地API。


4
第二个非常好,也适用于滚动条。我没有在平板电脑上测试就发布了我的应用程序,太兴奋了。现在更新正在进行中! - jksevend
2
否则这个问题就没有意义了...不一定。我需要水平滚动来使UI响应,当视图区域宽时占据整个宽度,当它变窄时可滚动。 - Stack Underflow

11

将你的数据表设置在容器中,并使容器的宽度double.infinity

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

8

对于 DataTable 小部件,以下代码可以让 dataTable 的宽度与设备宽度相匹配:

代码片段:

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

您可以使用属性,例如

grid-column-gap: 0;

来消除列之间的空格

 columnSpacing: 0,

注意:

使用 ConstrainedBox 组件可以解决你的问题,

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

完整代码:

注意: 在这个示例代码中,我涵盖了DataTable小部件的排序编辑概念。

在Lib文件夹中,您必须有这个类

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

main.dart类代码

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类代码

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

4

简单回答:

使用 width: double.infinity()Container() 包装您的数据表格。

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

我的首选方法

您可以在 pub.dev 上使用 DataTable 2 包
https://pub.dev/packages/data_table_2

此包将为您提供 DataTable2() widget,默认情况下会扩展到可用空间。此外,您还可以获得更多选项,例如 ColumnSize 等。


3

只需用SizedBox包装您的DataTable,并将宽度设置为double.infinity。

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

1

只需使用一个具有固定宽度定义的容器包装数据表,一切都应该正常工作。

即使您需要在一个屏幕上使用多个表格,在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
        )
    ])
  ))
)

这也适用于单个表格,只需使用所需宽度包装容器即可。

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

你可能想要添加更多细节,仅有代码的答案并不是很有用。 - mozway
代码已经说明了一切。我感觉没有必要解释它。 - Paulo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接