我有一个 2 列颤动数据表,并且线条不跨越屏幕宽度,留下大量空白。我发现了这个问题
https://github.com/flutter/flutter/issues/12775
建议将 DataTable 包装在 SizedBox.expand 小部件中,但这不起作用产生
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),
),
),
),
]))
);
}
您可以将
crossAxisAlignment
添加到您的 Column
来拉伸
crossAxisAlignment: CrossAxisAlignment.stretch
SizedBox.expand
导致 DataTable
采取无限高度,这是 SingleChildScrollView
不喜欢的。由于您只想跨越父级的宽度,因此可以使用 LayoutBuilder
来获取您关心的父级的大小,然后将 DataTable
包裹在 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: []),
),
),
),
],
),
),
),
);
}
这是一个问题,不完整,在一个美丽的小部件(数据表)中, 我在生产代码中遇到了这个问题,该解决方案适用于一半以上的实验室设备:
ConstrainedBox(
constraints: BoxConstraints.expand(
width: MediaQuery.of(context).size.width
),
child: DataTable( // columns and rows.),)
但是您知道什么在 %100 的设备上有效吗?这个:
Row( // a dirty trick to make the DataTable fit width
children: <Widget>[
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(...) ...]//row children
注意:Row 只有一个子 Expanded,它又包含一个 SingleChildScrollView,而 SingleChildScrollView 又包含 DataTable。
请注意,这样您就可以
t use SingleChileScrollView with scrollDirection: Axis.horizontal, in case you need it, but you don
否则这个问题将与您的用例无关。
如果 Flutter 团队的有人读到了这篇文章,请丰富一下 DataTable Widget,这将使 flutter 变得更有竞争力和强大,如果做得好的话,flutter 可能会超越 android 自己的原生 API。
在Container中设置您的datatable并将容器的width设置为double.infinity
Container(
width: double.infinity,
child: DataTable(
columns: _columns,
rows: _rows,
));
对于 DataTable 小部件,此代码对我有用,将 dataTable 宽度视为与设备宽度匹配的父级,
代码片段:
ConstrainedBox(
constraints:
BoxConstraints.expand(
width: MediaQuery.of(context).size.width
),
child:
DataTable(
// inside dataTable widget you must have columns and rows.),)
您可以使用之类的属性删除列之间的空间
columnSpacing: 0,
注意:
使用 ConstrainedBox 小部件解决了您的问题,
constraints: BoxConstraints.expand(width: MediaQuery.of(context).size.width),
完整代码:
注: 在此示例代码中,我介绍了排序和编辑数据表小部件概念。
在Lib文件夹中你必须有这个类
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),
];
}
}
简单答案:
用
Container()
和 width: double.infinity()
包裹数据表。
Container(
width: double.infinity,
child: DataTable(
..
.
我的首选方式
您可以在 pub.dev 使用 DataTable 2 包
https://pub.dev/packages/data_table_2
此软件包将为您提供
DataTable2()
小部件,默认情况下它将扩展到可用空间。您还可以获得更多选项,例如ColumnSize
等。
只需用 Sizedbox 包裹您的 DataTable 并将宽度设置为 double.infinity。
SizedBox(
width: double.infinity,
child: DataTable()
)
只需用定义了固定width的容器包装数据表,一切就可以工作了。
即使您需要在一个屏幕上显示多个表格,从 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
)
])
))
)
这也适用于单张桌子,只需用所需宽度的容器包裹即可。
SingleChildScrollView(
child: Card(
child: SizedBox(
width: double.infinity,
child: DataTable(columns:_columns, rows:_rows),
),
),
),
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(),
),
),
简单的解决方案是使用columnSpacing并使用这个公式(屏幕宽度/列数===屏幕宽度*(1/列数))即
columnSpacing: MediaQuery.of(context).size.width * 0.2
因为在我的例子中列计数为 5,因此(屏幕宽度 *(1/5 即 0.2))。
希望这有帮助不要忘记用 SingleChildScrollView 包装 DataTable