Flutter Wrap widget rendering glitches when height is updated

109 Views Asked by At

I am creating a form (not using the Form Widget) in Flutter where the user can add an arbitrary amount of items (treatments) which are rendered as InputChip widgets list in a Wrap widget.

The form uses a button (AddButton widget) which opens a form dialog which itself returns the newly created item (treatment) that is added to selectedItems:

class TreatmentsWidget extends StatefulWidget {
  const TreatmentsWidget({super.key, required this.selectedItems});

  final List<Treatment> selectedItems;

  @override
  State<TreatmentsWidget> createState() => _TreatmentsWidgetState();
}

class _TreatmentsWidgetState extends State<TreatmentsWidget> {
  @override
  Widget build(BuildContext context) {
    var chips = widget.selectedItems.map(
      (item) {
        return InputChip(
          label: Text('${item.name} - ${item.frequency}/${item.frequencyUnit.name})',
        );
      },
    ).toList();

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Wrap(
          children: chips,
        ),
        AddButton(onPressed: () {
          showDialog(
              context: context,
              builder: (BuildContext context) {
                return const TreatmentDialog();
              }).then((value) {
            if (value != null) {
              Treatment item = value;
              setState(() {
                widget.selectedItems.add(item);
              });
            }
          });
        }),
      ],
    );
  }
}

For some reason, when a new item is added to selectedItem and that the item overflows the current line, the layout is not recomputed such that the Wrap widget overflows the button:

2 chips

3 chips glitch

However, as soon as the user scroll (the whole screen content is inside a SingleChildScrollView), the layout is recomputed and the Wrap takes the right amount of space:

3 chips

How can I force a redraw when a new item is added to prevent this glitch?

1

There are 1 best solutions below

0
DurandA On

The issue seems to be that the Column does not recompute its size on the current frame when one of his child size changes.

I ended up forcing rebuilding the Column using a ValueKey whenever chips.length changes:

class _TreatmentsWidgetState extends State<TreatmentsWidget> {
  @override
  Widget build(BuildContext context) {
    var chips = ...;

    return Column(
      key: ValueKey(chips.length),
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Wrap(
          children: chips,
        ),
        AddButton(...),
      ],
    );
  }
}

I am still interested in a cleaner solution if it exists. Using a provider seems overkill for this case.