maxim-saplin / data_table_2

In-place substitute for Flutter's DataTable and PaginatedDataTable with fixed/sticky header and extra features
https://pub.dev/packages/data_table_2
BSD 3-Clause "New" or "Revised" License
202 stars 135 forks source link

DataTable2 with SliverMainAxisGroup/SliverToBoxAdapter non-zero flex but incoming height constraints are unbounded #276

Closed jeremy-giles closed 2 weeks ago

jeremy-giles commented 4 months ago

Hello, we are encountering a problem using data_table_2 when it is within a SliverMainAxisGroup / SliverToBoxAdapter.

SliverMainAxisGroup(
  slivers: [
    SliverToBoxAdapter(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(...),
    ),
    SliverToBoxAdapter(
      child: DataTable(
        columnSpacing: 12,
        horizontalMargin: 12,
        columns: generateColumns(),
        rows: generateRows(data),
      ),
    ),
  ],
)
screen

With Datatable there is no height problem but by simply adding "2", we get the error: Another exception was thrown: RenderFlex children have non-zero flex but incoming height constraints are unbounded.

We would like to be able to use data_table_2 which offers possibilities that we do not have with Datatable. What constraints would need to be applied so that the table uses a “necessary” height? A solution by applying a fixed height is not possible. All lines must be visible.

Here a complete example of a use of Sliver + Datatable :

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: CustomScrollView(
          slivers: [
            SliverTabSection(
              title: 'Safe room (3)',
              dataLines: List.generate(3, (i) => 'Safe room ${i + 1}'),
            ),
            const SliverDivider(),
            SliverTabSection(
              title: 'Stock room (1)',
              dataLines: ['Safe room 1'],
            ),
          ],
        ),
      ),
    );
  }
}

class SliverTabSection extends StatelessWidget {
  const SliverTabSection({
    required this.title,
    required this.dataLines,
  });

  final String title;
  final List<String> dataLines;

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: Future.delayed(Duration(seconds: 2)),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const SliverToBoxAdapter(
            child: Center(
              child: CircularProgressIndicator(),
            ),
          );
        } else if (snapshot.hasError) {
          return SliverToBoxAdapter(
            child: Text('Error: ${snapshot.error}'),
          );
        } else {
          return SliverMainAxisGroup(
            slivers: [
              SliverToBoxAdapter(
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Row(
                    children: [
                      Text(
                        title,
                        style: TextStyle(
                            fontSize: 24, fontWeight: FontWeight.bold),
                      ),
                      Text('+'),
                    ],
                  ),
                ),
              ),
              SliverToBoxAdapter(
                child: DataTable(
                  columns: const [DataColumn(label: Text('Nom'))],
                  rows: dataLines.map(
                    (label) {
                      return DataRow(
                        cells: <DataCell>[DataCell(Text(label))],
                        selected: false,
                        onSelectChanged: (_) {},
                      );
                    },
                  ).toList(),
                ),
              ),
            ],
          );
        }
      },
    );
  }
}

class SliverDivider extends StatelessWidget {
  const SliverDivider();

  @override
  Widget build(BuildContext context) {
    return SliverToBoxAdapter(
      child: const Divider(
        height: 16,
        thickness: 1,
        indent: 16,
        endIndent: 0,
        color: Colors.black,
      ),
    );
  }
}

Thanks in advance. Jérémy

maxim-saplin commented 4 months ago

The container for DataTable2 must have a definitive height, that is the whole idea of the widget - fit the rows area into the available height, keep the header fixed and have the widget handle scrolling of data rows.

You can try playing with intrinsic height widget or compute the parent height and set it to a valid value

jkoenig134 commented 3 months ago

@maxim-saplin I think this is correct for the paginated data table, but the data table itself doesn't have a footer, so I don't understand why it should fill all available height instead of letting the widget(s) above manage scroll and overflow like the original DataTable does.

I switched from DataTable to DataTable2 because of the very useful empty parameter, but it was way harder than it should be because the behavior highly differs between both implementations. When I got it right DataTable2 should be a drop-in replacement to DataTable, for me it didn't felt like that in that case.

maxim-saplin commented 3 months ago

The behavior difference between DataTable and DataTable2 in sliver layouts stems from DataTable2's design that necessitates knowing its available height upfront. This requirement enables features like fixed headers and internal scrolling but introduces a challenge in unbounded contexts like slivers. Developers transitioning from DataTable to DataTable2 need to be aware of this constraint and plan their layouts accordingly, ensuring that DataTable2 receives explicit or computed height constraints to function as intended.

tas-unn commented 3 months ago

how i can calculate heigth? i've dataRowHeight: 130, and i tried to calculate sizedbox heigth=130.0_rowsPerPage+300 if _rowsPerPage=10, then I have empty space at the bottom, and if _rowsPerPage=100, then no empty space. I tried 150.0_rowsPerPage - for 10 and 20 it’s ok, for 50 there’s not enough space.