
Flutter에서 리스트를 구성할 때 가장 많이 쓰는 구조 중 하나는 ListView.builder다.
그런데 shrinkWrap: true와 NeverScrollableScrollPhysics() 조합은 단순히 UI 구성에는 편리하지만, 대량 데이터를 다룰 때 심각한 성능 문제를 일으킬 수 있다.
대부분은 이런 구조에서다:
SingleChildScrollView(
child: Column(
children: [
Text("제목"),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: ...
),
],
),
)
위처럼 여러 위젯과 리스트를 함께 배치하려다 보니 shrinkWrap: true를 사용하게 되는데,
이 구조는 비효율적이다.
ListView.builder는 원래 lazy rendering, 즉 스크롤되기 전에는 위젯을 만들지 않는 방식으로 작동한다.
하지만 shrinkWrap: true를 사용하면 이 lazy 특성이 사라진다. 전체 데이터를 한 번에 렌더링하게 된다.
Column(
children: items.map((item) => Text(item)).toList(),
)
Column(
children: [
for (var item in items) Text(item),
],
)
이 코드도 결국 모든 아이템을 한 번에 렌더링한다.
즉, lazy rendering이 아닌 건 똑같다.
CustomScrollView는 여러 scrollable 위젯을 lazy하게 조합할 수 있게 도와준다.
가장 적절한 구조는 아래와 같다:
CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Text(items[index]),
childCount: items.length,
),
),
],
)
이 방식은 ListView.builder의 lazy 성능은 유지하면서도, 여러 위젯을 함께 구성할 수 있는 유연함을 제공한다.
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Text("제목"),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Text(items[index]),
childCount: items.length,
),
),
],
)
SliverToBoxAdapter → 일반 위젯을 포함시킬 때 사용SliverList → 리스트 아이템을 lazy하게 렌더링shrinkWrap: true는 필요할 때만 쓰고, 대량 데이터에는 반드시 피해야 한다.CustomScrollView + Sliver가 정답이다.