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

时间:2019-10-15 16:10:06

标签: flutter dart datatable

我已经使用DataTable在Flutter Dart上建立了一个表格。该表非常大,我同时使用“垂直”和“水平”滚动。 滚动时,我失去对列的引用,我需要知道什么是列。

例如。在屏幕快照上,除非滚动到顶部,否则我不知道数字20.025.0的含义。

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

表格示例,同时在表格中间滚动: Table without header for reference

我想做的事的例子: LibreOffice Calc Example

我的表的代码示例:

    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()
        )
      ),
    );

缺少代码示例:

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

更新(24/10/2019)

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

Different sizes

3 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:2)

我可以使用滚动控制器想出一种解决方法,如下所示:Video

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

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

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

由于第一列,第一行和子表是独立的,因此我必须为每个表创建一个DataTable。而且由于DataTable具有无法删除的标题,因此第一行和子表的标题被第一行隐藏。

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

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

答案 2 :(得分:2)

试试flutter包horizontal_data_table
一个 Flutter Widget,它创建一个水平表格,左侧固定列。

dependencies:
  horizontal_data_table: ^2.5.0

enter image description here