我想创建一系列可以垂直滚动的表,每个表可能具有彼此不同的行数/列数。
在每个表中,我希望冻结最左边的列,并且该表中的其余列可以水平滚动,以防万一某些列不适合屏幕宽度。查看屏幕截图:
我最初的计划是使用ListView在表之间进行页面级垂直滚动,并且在每个表中都有一列列,其中第一列是静态宽度,其余列包含在水平滚动ListView。我从Flutter收到的错误并不能帮助我确定需要做的事情,但显然与必须在子Widget上设置边界有关。
错误:(通过用固定高度的容器包装水平ListView并收缩包装ListView来修复19/7/9
)在performResize()期间引发了以下断言: 水平视口的宽度无限制。 视口沿滚动方向扩展以填充其容器。 视口被赋予了无限的水平空间来扩展。这个情况 通常在将可滚动小部件嵌套在另一个可滚动小部件内时发生。 如果此窗口小部件始终嵌套在可滚动窗口小部件中,则无需使用视口,因为 孩子们总是会有足够的水平空间。在这种情况下,请考虑使用行 代替。否则,请考虑使用“ shrinkWrap”属性(或ShrinkWrappingViewport)调整大小 视口的宽度与其子项的宽度之和。
新错误19年7月9日:
在布局期间引发了以下消息: RenderFlex在右侧溢出了74个像素。 溢出的RenderFlex的方向为Axis.horizontal。 RenderFlex溢出的边缘已在渲染中用黄色和 黑色条纹的图案。这通常是因为内容对于RenderFlex而言太大。 考虑应用弹性系数(例如,使用扩展小部件)来强制使用 RenderFlex可以容纳在可用空间中,而不是按照其自然大小进行调整。 这被认为是错误情况,因为它表明存在无法 看过。如果内容合法大于可用空间,请考虑使用 在将ClipRect小部件放入Flex中或使用可滚动容器而不是Flex之前, 像ListView。 有问题的特定RenderFlex是:
RenderFlex#9bf67 relayoutBoundary = up5 OVERFLOWING
创建者:行←RepaintBoundary-[<0>]←IndexedSemantics←
NotificationListener←KeepAlive←AutomaticKeepAlive←SliverList←
SliverPadding←视口←IgnorePointer- [GlobalKey#74513]←语义←侦听器←⋯
parentData :(可以使用大小)
约束:BoxConstraints(w = 404.0,0.0 <= h <= Infinity)
尺寸:尺寸(404.0,300.0)
方向:水平
mainAxisAlignment:开始
mainAxisSize:max
crossAxisAlignment:中心
textDirection:ltr
这是我最初遇到的问题,后来才被报道的第一个问题所困扰。我不明白为什么我的ListView没有创建可滚动的容器。
代码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: Scaffold(
appBar: AppBar(
title: Text('My App'),
backgroundColor: Colors.teal[400],
),
body: MyClass(),
),
);
}
}
const double headerCellWidth = 108.0;
const double cellPadding = 8.0;
const double focusedColumnWidth = 185.0;
const double rowHeight = 36.0;
class MyClass extends StatefulWidget {
@override
_MyClassState createState() => _MyClassState();
}
class _MyClassState extends State<MyClass> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.all(5.0),
children: <Widget>[
Row(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.grey,
padding: EdgeInsets.all(cellPadding),
width: headerCellWidth,
),
HeaderCell('ABC'),
HeaderCell('123'),
HeaderCell('XYZ'),
],
),
Container(
height: 300.0, // Could compute height with fixed rows and known number of rows in advance
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: <Widget>[
Column(
children: <Widget>[
Container(
color: Colors.grey[300],
padding: EdgeInsets.all(cellPadding),
height: rowHeight,
width: focusedColumnWidth,
),
NumberCell('89'),
NumberCell('92'),
NumberCell('91'),
NumberCell('90'),
NumberCell('91'),
NumberCell('89'),
],
),
Column(
children: <Widget>[
Container(
color: Colors.grey[300],
padding: EdgeInsets.all(cellPadding),
height: rowHeight,
width: focusedColumnWidth,
),
NumberCell('89'),
NumberCell('92'),
NumberCell('91'),
NumberCell('90'),
NumberCell('91'),
NumberCell('89'),
],
),
],
),
),
],
),
],
);
}
}
class HeaderCell extends StatelessWidget {
HeaderCell(this.text);
final String text;
@override
Widget build(BuildContext context) {
return Container(
height: rowHeight,
padding: EdgeInsets.all(cellPadding),
width: headerCellWidth,
child: Text(
text,
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
);
}
}
class NumberCell extends StatelessWidget {
NumberCell(this.text);
final String text;
@override
Widget build(BuildContext context) {
return Container(
height: rowHeight,
width: focusedColumnWidth,
padding: EdgeInsets.all(cellPadding),
child: Text(
text,
),
);
}
}
答案 0 :(得分:4)
这是一个简单的示例,其结果如下:Video
List<Widget> _buildCells(int count) {
return List.generate(
count,
(index) => Container(
alignment: Alignment.center,
width: 120.0,
height: 60.0,
color: Colors.white,
margin: EdgeInsets.all(4.0),
child: Text("${index + 1}", style: Theme.of(context).textTheme.title),
),
);
}
List<Widget> _buildRows(int count) {
return List.generate(
count,
(index) => Row(
children: _buildCells(10),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildCells(20),
),
Flexible(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildRows(20),
),
),
)
],
),
),
);
}
答案 1 :(得分:0)
您是否尝试将shrinkWrap: true
设置为水平listView?
答案 2 :(得分:0)
因此,我尝试产生最少的代码工作量,并最终得出了可行的解决方案(即使所有细节都没有解决,例如第一个锁定列的宽度是灵活的,而不是所需的固定宽度)。希望这将有助于其他人尝试产生类似的东西。有趣的是,这里需要Table构造,因为仅用Row替换TableRow(由Table包裹)会导致溢出错误。我仍然会对理解为什么如此感兴趣,因为它对于布局引擎而言至关重要。
@override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.all(5.0),
children: <Widget>[
Table(
children: <TableRow>[
TableRow(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// first locked column items
],
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
// table header items
],
),
Row(
children: <Widget>[
// data cells
],
),
Row(
children: <Widget>[
// data cells
],
),
],
),
),
],
),
],
),
],
);
}
答案 3 :(得分:0)
如果不需要太多自定义,则对于那些需要的自定义标头和第一列表,也可以考虑使用horizontal_data_table包: https://pub.dev/packages/horizontal_data_table
它基本上是使用两个列表视图的方法。