nank1ro/flutter-shadcn-ui

ShadTable width problems on Flutter Web using 'RemainingTableSpanExtent'

Closed this issue · 4 comments

Hello,

I implemented the ShadTable into a prototype project and experiencing an weird behaviour with the "RemainingTableSpanExtent".

On mobile the middle column of the table expands perfectly fine.
image

On web I experience the following:
image

Code:

import 'package:prototype_app/components/appbar.dart';
import 'package:prototype_app/components/timesheet_card.dart';
import 'package:prototype_app/theme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:shadcn_ui/shadcn_ui.dart';

var timesheets = [
  (
    date: DateTime.now(),
    startTime: "7:00",
    endTime: "16:00",
    timeBreak: "1:00",
  ),
  (
    date: DateTime.now(),
    startTime: "7:00",
    endTime: "16:00",
    timeBreak: "1:00",
  ),
  (
    date: DateTime.now(),
    startTime: "7:00",
    endTime: "16:00",
    timeBreak: "1:00",
  ),
  (
    date: DateTime.now(),
    startTime: "7:00",
    endTime: "16:00",
    timeBreak: "1:00",
  ),
];

final months = {
  'jan': 'Januar',
  'feb': 'Februrar',
  'mar': 'März',
  'apr': 'April',
  'may': 'Mai',
  'jun': 'Juni',
  'jul': 'July',
  'aug': 'August',
  'sep': 'September',
  'okt': 'Oktober',
  'nov': 'November',
  'dez': 'Dezember',
};

final years = {
  2024,
  2023,
  2022,
};

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

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);

    return Scaffold(
      appBar: AppBar(context, false),
      body: Padding(
        padding: const EdgeInsets.only(
          top: 20,
          left: 20,
          right: 20,
        ),
        child: SafeArea(
          child: Wrap(
            runSpacing: 15,
            children: [
              const TimesheetCard(
                overviewMode: false,
              ),
              Row(
                children: [
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.only(right: 8.0),
                      child: ShadCard(
                        content: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: [
                            Text(
                              "Überstunden",
                              style: theme.textTheme.large,
                            ),
                            Text(
                              "3:54",
                              style: theme.textTheme.h2,
                            ),
                            Text(
                              "2024",
                              style: theme.textTheme.p,
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.only(left: 8.0),
                      child: ShadCard(
                        content: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: [
                            Text(
                              "Vertrag",
                              style: theme.textTheme.large,
                            ),
                            Text(
                              "40:00",
                              style: theme.textTheme.h2,
                            ),
                            Text(
                              "Noch 13:00",
                              style: theme.textTheme.p,
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ],
              ),
              Padding(
                padding: const EdgeInsets.only(top: 12),
                child: Text("Bereits erfasst", style: theme.textTheme.h3),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Expanded(
                    child: ShadSelect<String>(
                      placeholder: const Text('Monat'),
                      options: [
                        Padding(
                          padding: const EdgeInsets.fromLTRB(32, 6, 6, 6),
                          child: Text(
                            'Monate',
                            style: theme.textTheme.muted.copyWith(
                              fontWeight: FontWeight.w600,
                              color: theme.colorScheme.popoverForeground,
                            ),
                            textAlign: TextAlign.start,
                          ),
                        ),
                        ...months.entries.map((e) =>
                            ShadOption(value: e.key, child: Text(e.value))),
                      ],
                      selectedOptionBuilder: (context, value) =>
                          Text(months[value]!),
                    ),
                  ),
                  Expanded(
                    child: ShadSelect<String>(
                      placeholder: const Text('Jahr'),
                      options: [
                        Padding(
                          padding: const EdgeInsets.fromLTRB(32, 6, 6, 6),
                          child: Text(
                            'Jahre',
                            style: theme.textTheme.muted.copyWith(
                              fontWeight: FontWeight.w600,
                              color: theme.colorScheme.popoverForeground,
                            ),
                            textAlign: TextAlign.start,
                          ),
                        ),
                        ...years.map((e) => ShadOption(
                            value: e.toString(), child: Text(e.toString()))),
                      ],
                      selectedOptionBuilder: (context, value) => Text(value),
                    ),
                  ),
                ],
              ),
              Center(
                child: ConstrainedBox(
                  constraints: const BoxConstraints(
                    maxWidth: 600,
                    // added just to center the table vertically
                    maxHeight: 700,
                  ),
                  child: ShadTable.list(
                    columnSpanExtent: (index) {
                      if (index == 0) return const FixedTableSpanExtent(80);
                      if (index == 1) {
                        return const MinTableSpanExtent(
                          FixedTableSpanExtent(150),
                          RemainingTableSpanExtent(),
                        );
                      }
                      return const FixedTableSpanExtent(130);
                    },
                    rowSpanExtent: (row) {
                      return const FixedTableSpanExtent(60);
                    },
                    children: timesheets.map(
                      (timesheet) => [
                        ShadTableCell(
                          alignment: Alignment.center,
                          child: Wrap(
                            crossAxisAlignment: WrapCrossAlignment.center,
                            direction: Axis.vertical,
                            children: [
                              Text(
                                "${timesheet.date.day}",
                                textAlign: TextAlign.center,
                                style: theme.textTheme.large,
                              ),
                              Text(
                                DateFormat('EE', 'de_DE')
                                    .format(timesheet.date)
                                    .replaceAll(".", ""),
                                textAlign: TextAlign.center,
                                style: theme.textTheme.p
                                    .copyWith(color: primaryColor),
                              ),
                            ],
                          ),
                        ),
                        ShadTableCell(
                          child: Wrap(
                            direction: Axis.vertical,
                            children: [
                              Text(
                                "${timesheet.startTime} - ${timesheet.endTime}",
                                style: theme.textTheme.large,
                              ),
                              Text(
                                "Pause: ${timesheet.timeBreak}",
                                style: theme.textTheme.p,
                              ),
                            ],
                          ),
                        ),
                        const ShadTableCell(
                            alignment: Alignment.center,
                            child: Row(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                ShadButton.secondary(
                                  padding: EdgeInsets.symmetric(horizontal: 8),
                                  icon: Icon(Icons.edit),
                                ),
                                ShadButton.destructive(
                                  padding: EdgeInsets.symmetric(horizontal: 8),
                                  icon: Icon(Icons.delete),
                                ),
                              ],
                            )),
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Can you try pointing to the latest main? I added the scroll physics to disable the scroll horizontally and vertically if needed.
I tried with:

ShadTable.list(
  horizontalScrollPhysics:
      const NeverScrollableScrollPhysics(),
  columnSpanExtent: (index) {
    if (index == 0) {
      return const FractionalTableSpanExtent(0.2);
    }
    if (index == 1) {
      return const FractionalTableSpanExtent(0.6);
    }
    return const FixedTableSpanExtent(130);
  },
  rowSpanExtent: (row) {
    return const FixedTableSpanExtent(60);
  },
  ...
),

On mobile you may still use the old extents. Use a way to determine if on web or mobile, or by using ShadResponsiveBuilder or by using a combination of MinTableSpanExtent and MaxTableSpanExtent

The FractionalTableSpanExtent should use the entire size available. So it should work even on mobile if the total reaches 1.0. I'm trying to understand why it doesn't work

This one seems working pretty fine on large sizes and even on small ones

 return FractionalTableSpanExtent(
                        index == 0
                            ? 0.1
                            : index == 1
                                ? 0.5
                                : 0.4,
                      );

Still overflows when the screen is to small but can be adjusted accordingly. Let me know if you have any other problem or if I can close the issue

Closing because the problem mentioned is not a bug. Feel free to open another issue if you find again the problem