flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.65k stars 27.35k forks source link

Stick header and scroll rows in DataTable #157305

Open insidesecurity-yhojann-aguilera opened 1 day ago

insidesecurity-yhojann-aguilera commented 1 day ago

Use case

By example: https://datatables.net/examples/basic_init/scroll_y.html

There are times when we need to have a very large list of data on horizontal screens like on a tablet, making pagination is not comfortable when you have a lot of data, instead of this it would be much more comfortable to render a simple scroll, but why not use the native scroll component of Flutter? because this scroll hides the entire component including the header and the DataTable theme is not applied to the external component.

For example, I want the theme to say that the DataTable components have a full border, that they are expanded, that they have an opaque background color and that the headers have a darker color, if I want to add a scroll to this I will see that the headers are hidden and that the borders do not adapt to the design, to achieve this I would have to transport all the style of the table to an external component above the scroll, then I would lose all sense of theme management within Flutter and the scroll would not be adjusted to the content of the component but to the entire block.

Since the early 90s until today, with the exception of Flutter, almost all data grid components such as in Google Sheets or Excel, while browsing the data you do not lose sight of the headers, while the user goes down and down numerical data, he will lose sight of which data is each column.

Based on user experience and compatibility with large screens, I believe that a more native handling of the body data scroll from within the DataTable control is necessary, understanding that it is a component intended to write large amounts of data, the use of this component without a scroll that maintains the visibility context is inconceivable.

For a moment I tried to use PaginatedDataTable but it has a design problem which uses a static height defined by default, reason why it is not possible to expand like any other control because by doing so I would have to guess the amount of cells to show and would never fit an exact height amount for the whole component, not to mention the desktop version where the window can be resized, this type of table does not resize vertically dynamically, the only way it could resize dynamically is with a native and internal scroll to the data which it does not incorporate, putting a scroll outside the table component would mean that the border design would be lost and the headers too.

Another problem I notice is that the data exceeds the region of the component but the overflow is not controlled, this would not happen if the content was restricted by the height of the component with an internal scroll. I think you can reuse the same component by adding a native scroll inside it and enabling it from an option, where it is disabled by default, in this way the component should be expanded with its background color and the rows should appear from above and when it exceeds the vertical space the scroll appears, if you put a scroll outside the DataTable the height is cut and the expansion effect does not work anymore:

Expanded(child: DataTable(
                            columns: [

image

image

Expanded(child: SingleChildScrollView(
                                physics: const BouncingScrollPhysics(),
                                scrollDirection: Axis.vertical,
                                child: DataTable(

image

I try use two DataTable, first for head and last for body rows with a parent scroll and root container with a box decoration but have problems with the column width, by default is dynamic to fit the content, need a event function or controller for change the each row width of the header in same time of the boyd rows, but DataTable does not support it.

In https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/material/data_table.dart#L1182 can replace:

        child: Table(
          columnWidths: tableColumns.asMap(),
          defaultVerticalAlignment: TableCellVerticalAlignment.middle,
          children: tableRows,
          border: border,
        ),

By:

                child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                        Table(
                            columnWidths: tableColumns.asMap(),
                            defaultVerticalAlignment: TableCellVerticalAlignment.middle,
                            children: <TableRow>[ tableRows[0] ],
                            border: border,
                        ),
                        Expanded(
                            child: SingleChildScrollView(
                                scrollDirection: Axis.vertical,
                                child: Table(
                                    columnWidths: tableColumns.asMap(),
                                    defaultVerticalAlignment: TableCellVerticalAlignment.middle,
                                    children: tableRows.sublist(1),
                                    border: border,
                                )
                            ),
                        ),
                    ]
                ),

And works fine!, but the column width are not synchronized:

image

Only works with a static column with using columnWidths property, but it is not sane for the dynamic content.

For now, use a single Table component with all rows and wraped in a SingleChildScrollView as child of Material, the header is lost but the theme and table works fine.

image

Proposal

https://github.com/herbertamukhuma/scrollable_table_view

This component works fine but does not support themes or dark mode.

darshankawar commented 15 hours ago

@insidesecurity-yhojann-aguilera Can you check if this https://github.com/flutter/flutter/issues/30770 resembles your case or not ? If not, how different is your use case / proposal from the one that was fixed in above issue ?

insidesecurity-yhojann-aguilera commented 9 hours ago

That's right, I see that it is the same, but for now I have temporarily solved it by creating a clone of the t component by adding the scroll to the Table of the render, works fine for now.

        return Container(
            decoration: decoration ?? dataTableTheme.decoration ?? theme.dataTableTheme.decoration,
            child: Material(
                type: MaterialType.transparency,
                borderRadius: border?.borderRadius,
                clipBehavior: clipBehavior,

                child: SingleChildScrollView(
                    scrollDirection: Axis.vertical,
                    child: Table(
                        columnWidths: tableColumns.asMap(),
                        defaultVerticalAlignment: TableCellVerticalAlignment.middle,
                        children: tableRows,
                        border: border,
                    ),
                ),
            );

I will wait for the official implementation of stick the header.