Flutter Dart 上 DataTable 的固定列和行标题

Gus*_*cia 10 datatable dart flutter

我已经使用DataTable. 这个表非常大,我同时使用垂直和水平滚动。滚动时我失去了对列的引用,我需要知道列是什么。

作为例子。在屏幕截图上,除非我滚动到顶部,否则我不知道数字20.025.0含义。

我添加了一个我想要实现的 GIF 示例。(使用 LibreOffice)。我需要固定的列名(第一行)。

表格示例,在表格中间滚动时: 无标题表供参考

我想做的例子: LibreOffice Calc 示例

我的表的代码示例:

    return SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: DataTable(
          columns: MyDataSet.getColumns(),
          rows: widget._data.map<DataRow>((row) => DataRow(
            onSelectChanged: (d) {
              setState(() {
                selectedRow = d ? row.hashCode : null;
              });
            },
            selected: row.hashCode == selectedRow,
            cells: MyDataSet.toDataCells(row)
          )).toList()
        )
      ),
    );
Run Code Online (Sandbox Code Playgroud)

缺少代码示例:

return columns.map<DataColumn>((name) => DataColumn(
      label: Text(name, style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black),)
    )).toList();
Run Code Online (Sandbox Code Playgroud)

更新 (24/10/2019)

如果标题名称与单元格内容的大小相同,则当前代码运行良好。否则两种尺寸都会不同。

不同尺寸


更新 (21/02/2020)

人们创建了一个包来做到这一点。:D

https://pub.dev/packages/table_sticky_headers

图片来自 pub.dev! Pub.dev 示例图像。

Max*_*lin 10

几个月前,我遇到了类似的问题,即库存 DataTable 和 PaginationDataTable2 小部件的功能有限,不允许修复标题。最终,我采用了这些小部件并创建了我自己的版本,但具有二十一点和固定标题行。这是 pub.dev 上的插件: https ://pub.dev/packages/data_table_2

DataTable2 和 PaginatoredDataTable2 类提供与原始版本完全相同的 API。

注意:这些仅实现粘性顶行,最左边的列不是固定/粘性的


Pab*_*era 9

我可以想出一个使用滚动控制器的解决方法,如下所示:视频

基本上它是第一行的水平滚动,第一列的垂直滚动和子表的混合水平和垂直滚动。然后当你移动子表时,它的控制器移动列和行。

这是一个自定义小部件,其中包含如何使用它的示例:

final _rowsCells = [
  [7, 8, 10, 8, 7],
  [10, 10, 9, 6, 6],
  [5, 4, 5, 7, 5],
  [9, 4, 1, 7, 8],
  [7, 8, 10, 8, 7],
  [10, 10, 9, 6, 6],
  [5, 4, 5, 7, 5],
  [9, 4, 1, 7, 8],
  [7, 8, 10, 8, 7],
  [10, 10, 9, 6, 6],
  [5, 4, 5, 7, 5],
  [9, 4, 1, 7, 8],
  [7, 8, 10, 8, 7],
  [10, 10, 9, 6, 6],
  [5, 4, 5, 7, 5],
  [9, 4, 1, 7, 8]
];
final _fixedColCells = [
  "Pablo",
  "Gustavo",
  "John",
  "Jack",
  "Pablo",
  "Gustavo",
  "John",
  "Jack",
  "Pablo",
  "Gustavo",
  "John",
  "Jack",
  "Pablo",
  "Gustavo",
  "John",
  "Jack",
];
final _fixedRowCells = [
  "Math",
  "Informatics",
  "Geography",
  "Physics",
  "Biology"
];

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(),
    body: CustomDataTable(
      rowsCells: _rowsCells,
      fixedColCells: _fixedColCells,
      fixedRowCells: _fixedRowCells,
      cellBuilder: (data) {
        return Text('$data', style: TextStyle(color: Colors.red));
      },
    ),
  );
}

class CustomDataTable<T> extends StatefulWidget {
  final T fixedCornerCell;
  final List<T> fixedColCells;
  final List<T> fixedRowCells;
  final List<List<T>> rowsCells;
  final Widget Function(T data) cellBuilder;
  final double fixedColWidth;
  final double cellWidth;
  final double cellHeight;
  final double cellMargin;
  final double cellSpacing;

  CustomDataTable({
    this.fixedCornerCell,
    this.fixedColCells,
    this.fixedRowCells,
    @required this.rowsCells,
    this.cellBuilder,
    this.fixedColWidth = 60.0,
    this.cellHeight = 56.0,
    this.cellWidth = 120.0,
    this.cellMargin = 10.0,
    this.cellSpacing = 10.0,
  });

  @override
  State<StatefulWidget> createState() => CustomDataTableState();
}

class CustomDataTableState<T> extends State<CustomDataTable<T>> {
  final _columnController = ScrollController();
  final _rowController = ScrollController();
  final _subTableYController = ScrollController();
  final _subTableXController = ScrollController();

  Widget _buildChild(double width, T data) => SizedBox(
      width: width, child: widget.cellBuilder?.call(data) ?? Text('$data'));

  Widget _buildFixedCol() => widget.fixedColCells == null
      ? SizedBox.shrink()
      : Material(
          color: Colors.lightBlueAccent,
          child: DataTable(
              horizontalMargin: widget.cellMargin,
              columnSpacing: widget.cellSpacing,
              headingRowHeight: widget.cellHeight,
              dataRowHeight: widget.cellHeight,
              columns: [
                DataColumn(
                    label: _buildChild(
                        widget.fixedColWidth, widget.fixedColCells.first))
              ],
              rows: widget.fixedColCells
                  .sublist(widget.fixedRowCells == null ? 1 : 0)
                  .map((c) => DataRow(
                      cells: [DataCell(_buildChild(widget.fixedColWidth, c))]))
                  .toList()),
        );

  Widget _buildFixedRow() => widget.fixedRowCells == null
      ? SizedBox.shrink()
      : Material(
          color: Colors.greenAccent,
          child: DataTable(
              horizontalMargin: widget.cellMargin,
              columnSpacing: widget.cellSpacing,
              headingRowHeight: widget.cellHeight,
              dataRowHeight: widget.cellHeight,
              columns: widget.fixedRowCells
                  .map((c) =>
                      DataColumn(label: _buildChild(widget.cellWidth, c)))
                  .toList(),
              rows: []),
        );

  Widget _buildSubTable() => Material(
      color: Colors.lightGreenAccent,
      child: DataTable(
          horizontalMargin: widget.cellMargin,
          columnSpacing: widget.cellSpacing,
          headingRowHeight: widget.cellHeight,
          dataRowHeight: widget.cellHeight,
          columns: widget.rowsCells.first
              .map((c) => DataColumn(label: _buildChild(widget.cellWidth, c)))
              .toList(),
          rows: widget.rowsCells
              .sublist(widget.fixedRowCells == null ? 1 : 0)
              .map((row) => DataRow(
                  cells: row
                      .map((c) => DataCell(_buildChild(widget.cellWidth, c)))
                      .toList()))
              .toList()));

  Widget _buildCornerCell() =>
      widget.fixedColCells == null || widget.fixedRowCells == null
          ? SizedBox.shrink()
          : Material(
              color: Colors.amberAccent,
              child: DataTable(
                  horizontalMargin: widget.cellMargin,
                  columnSpacing: widget.cellSpacing,
                  headingRowHeight: widget.cellHeight,
                  dataRowHeight: widget.cellHeight,
                  columns: [
                    DataColumn(
                        label: _buildChild(
                            widget.fixedColWidth, widget.fixedCornerCell))
                  ],
                  rows: []),
            );

  @override
  void initState() {
    super.initState();
    _subTableXController.addListener(() {
      _rowController.jumpTo(_subTableXController.position.pixels);
    });
    _subTableYController.addListener(() {
      _columnController.jumpTo(_subTableYController.position.pixels);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Row(
          children: <Widget>[
            SingleChildScrollView(
              controller: _columnController,
              scrollDirection: Axis.vertical,
              physics: NeverScrollableScrollPhysics(),
              child: _buildFixedCol(),
            ),
            Flexible(
              child: SingleChildScrollView(
                controller: _subTableXController,
                scrollDirection: Axis.horizontal,
                child: SingleChildScrollView(
                  controller: _subTableYController,
                  scrollDirection: Axis.vertical,
                  child: _buildSubTable(),
                ),
              ),
            ),
          ],
        ),
        Row(
          children: <Widget>[
            _buildCornerCell(),
            Flexible(
              child: SingleChildScrollView(
                controller: _rowController,
                scrollDirection: Axis.horizontal,
                physics: NeverScrollableScrollPhysics(),
                child: _buildFixedRow(),
              ),
            ),
          ],
        ),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

由于第一列、第一行和子表是独立的,我不得不DataTable为每个创建一个。由于DataTable有无法删除的标题,第一列和子表的标题被第一行隐藏。

另外,我必须使第一列和第一行不可手动滚动,因为如果滚动它们,子表将不会滚动。

这可能不是最好的解决方案,但目前似乎不是另一种方法。您可以尝试改进这种方法,也许使用Table或 其他小部件而不是DataTable至少您可以避免隐藏子表和第一列的标题。


And*_*_id 2

尝试 flutter 包Horizo​​ntal_data_table
一个 Flutter Widget,它创建一个左侧有固定列的水平表。

dependencies:
  horizontal_data_table: ^2.5.0
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 这确实是 2021 年最好的解决方案。它解决了在不使用固定列大小的情况下无法修复标题的问题。我会将我的应用程序转换为该表并进行测试。 (2认同)