티스토리 뷰

Flutter는 다양한 UI 구성 요소를 제공하며, 그 중에서도 긴 리스트를 처리하는 데 최적화된 기능은 앱 개발자에게 필수적인 도구입니다. 이번 글에서는 ListView.builder 를 활용하여 긴 리스트를 효율적으로 관리하는 방법과 주요 구현 방식을 살펴봅니다.

참고. Work with long lists

긴 리스트란 무엇인가?

긴 리스트는 일반적으로 스크롤을 통해 무수히 많은 데이터를 보여주는 UI 구성 요소를 의미합니다. Flutter에서 기본 ListView는 소규모 리스트에 적합하지만, 수천 개 이상의 항목을 처리해야 할 때는 효율적인 메모리 관리가 필요합니다.

ListView.builder를 사용해야 하는 이유

  1. 성능 최적화:
    • ListView의 기본 생성자는 모든 리스트 항목을 메모리에 로드합니다.
    • 반면, ListView.builder는 화면에 표시되는 항목만 생성하여 메모리와 성능을 효율적으로 관리합니다.
  2. 무한 리스트 구현 가능:
    • 데이터가 계속 추가되는 무한 스크롤 리스트 구현에 유리합니다.

구현 방법

1. 데이터 소스 생성

리스트를 구성하기 위해 데이터 소스가 필요합니다. 아래는 10,000개의 아이템을 생성하는 예시입니다.

List<String>.generate(10000, (i) => 'Item $i');

2. 데이터를 위젯으로 변환

ListView.builder를 사용하여 데이터를 위젯으로 변환합니다. 각 항목은 ListTile로 렌더링됩니다.

ListView.builder(
  itemCount: items.length,
  prototypeItem: ListTile(
    title: Text(items.first),
  ),
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
    );
  },
);

ListView.builder의 주요 옵션

  1. itemCount:
    • 리스트의 총 항목 수를 정의합니다.
  2. itemBuilder:
    • 각 항목의 위젯을 생성하는 함수입니다. index를 활용해 항목별 데이터를 동적으로 제공할 수 있습니다.
  3. prototypeItem:
    • 리스트의 모든 항목이 동일한 크기를 가지는 경우, 프로토타입 항목을 설정하면 성능이 더욱 향상됩니다.
  4. itemExtent 또는 itemExtentBuilder:
    • 고정 크기의 항목은 itemExtent를 사용하고, 가변 크기의 항목은 itemExtentBuilder를 사용합니다.

실무 활용 팁

  • 고정 크기 리스트 사용:
    항목 크기가 일정하다면, itemExtentprototypeItem을 사용하여 성능을 더욱 최적화하세요.
  • 데이터 무한 로드:
    페이징(Paging)이나 무한 스크롤 기능을 구현할 때, ListView.builder와 함께 데이터 요청 로직을 추가하면 효과적입니다.
  • UI 커스터마이징:
    각 항목을 ListTile 외에도 이미지, 버튼 등으로 커스터마이징하여 사용자 경험을 높일 수 있습니다.

ListView.builder의 한계와 대안

  1. 복잡한 리스트:
    • 여러 유형의 항목이 필요한 경우, ListView 대신 CustomScrollViewSliverList를 고려해 보세요.
  2. 스크롤 성능:
    • 매우 긴 리스트의 경우, 캐싱이나 데이터 로딩 최적화를 추가로 고려해야 합니다.

결론: ListView.builder로 앱 성능 극대화

ListView.builder는 긴 리스트를 효율적으로 관리하고 성능을 최적화할 수 있는 Flutter의 강력한 도구입니다. 간단한 구현에서부터 무한 스크롤까지 다양한 요구 사항에 대응할 수 있어 실무에서 필수적인 요소입니다.

 

import 'package:flutter/material.dart';

void main() {
  runApp(
    MyApp(
      items: List<String>.generate(10000, (i) => 'Item $i'),
    ),
  );
}

class MyApp extends StatelessWidget {
  final List<String> items;

  const MyApp({super.key, required this.items});

  @override
  Widget build(BuildContext context) {
    const title = 'Long List';

    return MaterialApp(
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(title),
        ),
        body: ListView.builder(
          itemCount: items.length,
          prototypeItem: ListTile(
            title: Text(items.first),
          ),
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(items[index]),
            );
          },
        ),
      ),
    );
  }
}