I put a ListView.builder inside a Column under a header and the screen goes red:
Vertical viewport was given unbounded height.
Viewports expand in the scrolling direction to fill their container.
Where should the list go?
DEV3 Developer Forum
Every post is reviewed first. Usually reviewed within 24 hours.
I put a ListView.builder inside a Column under a header and the screen goes red:
Vertical viewport was given unbounded height.
Viewports expand in the scrolling direction to fill their container.
Where should the list go?
Why it happens
A Column gives its children unbounded height. A ListView wants to expand to fill whatever height it is given, so with infinite height it has nothing to size itself against and throws.
Fix 1 (almost always right): Expanded
Column(
children: [
const Header(),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, i) => ItemTile(items[i]),
),
),
],
)
The list now gets exactly the remaining height and stays lazy (only visible items are built).
Fix 2: shrinkWrap for short lists
ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [...],
)
shrinkWrap: true measures every child, so it is O(n) and defeats lazy building. Use it only for a handful of items (for example a list of 5 rows inside a bigger scrolling page).
Fix 3: one scroll view with slivers
If the header and the list should scroll together, use a CustomScrollView with SliverToBoxAdapter for the header and SliverList.builder (or SliverList(delegate: SliverChildBuilderDelegate(...)) on older versions) for the items. This is the cleanest answer for mixed content and keeps lazy building.
Pitfalls
GridView and for a horizontal ListView inside a Row; the fix is identical (Expanded, or a fixed SizedBox(height: ...) for a horizontal strip).