Saltar al contenido

Encabezado fijo de columna y fila para DataTable en Flutter Dart

Solución:

Podría encontrar una solución alternativa usando controladores de desplazamiento, se ve así: Video

Básicamente es un desplazamiento horizontal para la primera fila, un desplazamiento vertical para la primera columna y un desplazamiento horizontal y vertical mixto para la subtabla. Luego, cuando mueve la subtabla, sus controladores mueven la columna y la fila.

Aquí hay un widget personalizado con un ejemplo de cómo usarlo:

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

Dado que la primera columna, la primera fila y la subtabla son independientes, tuve que crear una DataTable para cada uno. Y desde DataTable tiene encabezados que no se pueden eliminar, los encabezados de la primera columna y la subtabla están ocultos por la primera fila.

Además, tuve que hacer que la primera columna y la primera fila no se puedan desplazar manualmente porque si las desplaza, la subtabla no se desplazará.

Puede que esta no sea la mejor solución, pero por el momento no parece haber otra forma de hacerlo. Podría intentar mejorar este enfoque, tal vez utilizando Table u otros widgets en lugar de DataTable al menos podría evitar ocultar los encabezados de la subtabla y la primera columna.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *