如何在 Flutter 中创建具有固定列的水平滚动表格?

     2023-02-23     149

关键词:

【中文标题】如何在 Flutter 中创建具有固定列的水平滚动表格?【英文标题】:How to create a horizontally scrolling table with fixed column in Flutter? 【发布时间】:2019-11-18 09:44:10 【问题描述】:

我想创建一系列可以垂直滚动的表格,每个表格的行数/列数可能不同。

在每个表格中,我希望将最左边的列冻结在适当的位置,并且该表格中的其余列可以水平滚动,以防有许多列不适合屏幕的宽度。见截图:

我最初的计划是使用 ListView 进行表格之间的页面级垂直滚动,并且在每个表格中,有一个 Row of Columns,其中第一列是静态宽度,其余列包含在一个水平滚动 ListView。我从 Flutter 得到的错误并没有帮助我确定我需要做什么,但它显然与必须设置子 Widget 的边界有关。

错误:(通过使用固定高度容器包装水平 ListView 并收缩包装 ListView 修复了 7/9/19)

在 performResize() 期间抛出了以下断言: 水平视口被赋予了无限的宽度。 视口在滚动方向上扩展以填充其容器。在这种情况下,水平 视口被赋予了无限量的水平空间来扩展。这个情况 当一个可滚动的小部件嵌套在另一个可滚动的小部件中时,通常会发生这种情况。 如果此小部件始终嵌套在可滚动小部件中,则无需使用视口,因为 孩子们总会有足够的水平空间。在这种情况下,请考虑使用 Row 反而。否则,请考虑使用“shrinkWrap”属性(或 ShrinkWrappingViewport)来调整大小 视口的宽度与其子项的宽度之和。

新错误 7/9/19:

在布局期间抛出以下消息: 一个 RenderFlex 在右侧溢出了 74 个像素。 溢出的 RenderFlex 的方向为 Axis.horizo​​ntal。 溢出的 RenderFlex 边缘已在渲染中用黄色和 黑色条纹图案。这通常是由于内容对于 RenderFlex 来说太大了。 考虑应用弹性因子(例如,使用 Expanded 小部件)来强制 RenderFlex 以适应可用空间,而不是按其自然大小调整大小。 这被认为是一种错误情况,因为它表明存在不能被 见过。如果内容合法地大于可用空间,请考虑使用 在将 ClipRect 小部件放入 flex 之前,或者使用可滚动容器而不是 Flex, 像一个列表视图。 有问题的具体 RenderFlex 是: RenderFlex#9bf67 relayoutBoundary=up5 溢出 创建者:行 ← RepaintBoundary-[] ← IndexedSemantics ← NotificationListener ← KeepAlive ← AutomaticKeepAlive ← SliverList ← SliverPadding ← 视口 ← IgnorePointer-[GlobalKey#74513] ← 语义 ← 监听器 ← ⋯ parentData:(可以使用大小) 约束:BoxConstraints(w=404.0, 0.0 尺寸:尺寸(404.0, 300.0) 方向:水平 mainAxisAlignment:开始 mainAxisSize: 最大值 crossAxisAlignment:居中 文本方向: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,
      ),
    );
  

【问题讨论】:

随着时间的推移,嵌套的行和列会变得混乱且难以阅读。您是否尝试过使用 Table 类:api.flutter.dev/flutter/widgets/Table-class.html 我将看看使用表格而不是嵌套的行/列;最初我关闭了一个表格,因为它不是用于滚动的,而且我必须支持 3 个列宽,并且只有部分表格是可滚动的(如上面的屏幕截图所示,最左边的列被锁定)。 【参考方案1】:

这是一个简单的示例,结果如下: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),
              ),
            ),
          )
        ],
      ),
    ),
  );

【讨论】:

很好的解决方案,谢谢巴勃罗。看起来这里的不同之处在于您将水平滚动部分包裹在一个灵活的小部件中,这可能是我遇到的问题;我无法想象使用 ListView 与 SingleChildScrollView 作为最顶层元素会在我们的示例之间产生差异。接受您的回复,因为它比我的解决方案更干净。 如果我们想使用扩展面板代替数据表数据怎么办?知道如何通过两侧平行滚动来实现吗? @praveenDp 你解决了吗,两边平行滚动,如果是请分享【参考方案2】:

所以我尝试生成最少的工作代码,并最终得到一个可行的解决方案(即使所有细节都没有解决,比如第一个锁定的列具有灵活的宽度而不是所需的固定宽度)。希望这将有助于其他人尝试制作类似的东西。有趣的是这里需要 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】:

如果不需要太多自定义,对于那些需要固定标题和第一列的表也可以考虑使用 Horizo​​ntal_data_table 包: https://pub.dev/packages/horizontal_data_table

它基本上是使用两个列表视图的方法。

【讨论】:

如何在 Flutter 中创建垂直滚动菜单效果

】如何在Flutter中创建垂直滚动菜单效果【英文标题】:HowtocreateaverticalscrollingmenueffectinFlutter【发布时间】:2021-02-2216:01:21【问题描述】:这个问题是对this问题的改进。在这种情况下,我想重现您可以在我们基础的this左侧看到的... 查看详情

在 Flutter 中创建双向 PageView 滚动?

】在Flutter中创建双向PageView滚动?【英文标题】:CreatingbidirectionalPageViewscrollinginFlutter?【发布时间】:2018-12-1815:50:57【问题描述】:您将如何在Flutter中实现双向页面滚动功能?给定Scaffold中的多个PageViews似乎不起作用,嵌套的Pag... 查看详情

如何在颤动中创建具有固定宽度和高度的颜色框?

】如何在颤动中创建具有固定宽度和高度的颜色框?【英文标题】:Howtocreatecolourboxwithfixedwidthandheightinflutter?【发布时间】:2018-07-2904:44:44【问题描述】:我正在尝试创建一个具有固定宽度和高度的颜色框。如何做到这一点?【... 查看详情

如何从具有小数类型列的主题在 ksql 中创建流

】如何从具有小数类型列的主题在ksql中创建流【英文标题】:howtocreatestreaminksqlfromtopicwithdecimaltypecolumn【发布时间】:2020-01-0922:21:05【问题描述】:我想从监控mysql表的kafka主题创建一个流。mysql表具有十进制(16,4)类型的列,... 查看详情

java示例代码_如何在Java中创建具有可变列的数据库表

java示例代码_如何在Java中创建具有可变列的数据库表 查看详情

如何在 Flutter 中创建同时具有 kg 和 lbs 选项的 Cupertino 选择器

】如何在Flutter中创建同时具有kg和lbs选项的Cupertino选择器【英文标题】:HowtocreateCupertinopickerthathasbothkgandlbsoptioninFlutter【发布时间】:2021-12-2716:10:25【问题描述】:我正在尝试创建一个CupertinoPicker来显示重量和其他选项,例如kg... 查看详情

如何在颤动中创建可滚动的行

】如何在颤动中创建可滚动的行【英文标题】:Howtocreatescrollablerowinflutter【发布时间】:2020-09-0520:17:02【问题描述】:我尝试在颤振中创建可滚动行但在尝试了多种方法后,我面临高度固定或列表不滚动的问题发布我尝试过的代... 查看详情

如何在 C++ 中从现有 2D 向量中创建具有特定列的新向量

】如何在C++中从现有2D向量中创建具有特定列的新向量【英文标题】:Howtocreateanewvectorwithparticularcolumnsfromexisting2Dvectorinc++【发布时间】:2014-04-0808:13:26【问题描述】:我有很多列(m*n)的2D向量(vector&lt;vector&lt;string&gt;&gt... 查看详情

在 Flutter 中创建一个双向无限 ListView.builder

】在Flutter中创建一个双向无限ListView.builder【英文标题】:CreateabidirectionalinfiniteListView.builderinFlutter【发布时间】:2021-06-2701:21:26【问题描述】:我的意思是在两个轴上,水平和垂直。我尝试嵌套两个ListView.builder,但它们没有按... 查看详情

如何在 Flutter 中创建具有动态大小和动画项目添加和删除的虚拟化、可重新排序的列表?

】如何在Flutter中创建具有动态大小和动画项目添加和删除的虚拟化、可重新排序的列表?【英文标题】:Howtocreateavirtualized,reorderablelistwithdynamicsizes,andanimateditemaddingandremovalinFlutter?【发布时间】:2021-07-3122:29:43【问题描述】:我... 查看详情

在 iOS 7 中使用 UIScrollView 在 iOS 中创建水平滚动 UIButton

...一个水平滚动按钮,我不知道这是正确的方法,请告诉我如何制作它这是我尝试以编程方式创建水平uibutton的代码, 查看详情

如何在flutter android开发中创建这种布局?

】如何在flutterandroid开发中创建这种布局?【英文标题】:howtocreatethiskindoflayoutinflutterandroiddevelopment?【发布时间】:2021-07-2618:35:16【问题描述】:Thisisasettingpagehowtomakethiskindoflayout?【问题讨论】:在您的查询中提供更多详细信息... 查看详情

如何在 Swift IOS 中创建具有多个堆栈视图的可滚动堆栈视图

】如何在SwiftIOS中创建具有多个堆栈视图的可滚动堆栈视图【英文标题】:HowtocreatescrollablestackviewwithmultiplestackviewsinswiftIOS【发布时间】:2016-09-2613:37:54【问题描述】:我正在尝试创建一个可滚动的堆栈视图。例如,它的层次结... 查看详情

无法在flutter web的有限高度容器中创建滚动

】无法在flutterweb的有限高度容器中创建滚动【英文标题】:Can\'tcreateascrollinalimitedheightcontainerinflutterweb【发布时间】:2021-03-0802:45:07【问题描述】:我在频道beta中使用颤振1.12.13+hotfix.6。我想在一个高度有限的容器中创建一个滚... 查看详情

如何在没有滚动条的 Tailwind 中创建可滚动元素

】如何在没有滚动条的Tailwind中创建可滚动元素【英文标题】:HowtocreatescrollableelementinTailwindwithoutascrollbar【发布时间】:2021-05-3015:38:59【问题描述】:我正在尝试在底部重新创建一个水平滚动导航栏没有滚动条,就像这个例子一... 查看详情

如何在颤动中创建具有多行的行

...建具有多行的行【英文标题】:Howtocreaterowwithmultiplelinesinflutter【发布时间】:2021-06-1216:18:14【问题描述】:我正在尝试复制一个类似于this的颤振设计,我能够通过使用Listtile小部件实现类似的效果,我唯一的问题是前导图标没... 查看详情

在 Flutter 中创建一个空数组并将其发送到具有空值的 Firestore?

】在Flutter中创建一个空数组并将其发送到具有空值的Firestore?【英文标题】:CreateanemptyarrayandsendittoFirestorewithanemptyvalueinFlutter?【发布时间】:2021-01-1917:24:49【问题描述】:我想在提交提要时在Firestore中创建一个空数组。但是该... 查看详情

如何在 QTableView 中创建冻结的页脚行

】如何在QTableView中创建冻结的页脚行【英文标题】:howtocreatefrozenfooterrowinQTableView【发布时间】:2014-02-1218:06:30【问题描述】:我知道有类似的问题,但没有一个有解决方案。Qt文档为QAbstractScrollArea::setViewportMargins()指定了这一... 查看详情