티스토리 뷰

Flutter는 단일 유형의 아이템뿐만 아니라 다양한 유형의 데이터를 표시할 수 있는 기능을 제공합니다. 리스트에 헤더, 메시지, 섹션 구분선 등을 함께 표시하려면 ListView와 사용자 정의 클래스를 결합하여 혼합형 리스트를 구현할 수 있습니다. 이번 글에서는 이러한 혼합형 리스트를 구현하는 방법과 주요 포인트를 설명합니다.

참고. Create lists with different types of items

혼합형 리스트란?

혼합형 리스트는 서로 다른 유형의 데이터를 포함하는 리스트를 의미합니다. 예를 들어:

  • 헤더: 섹션 구분을 위한 제목.
  • 메시지: 사용자 이름과 본문 텍스트.
  • 이미지 또는 버튼: 기타 인터랙티브 요소.

이러한 리스트는 단순한 텍스트 나열보다 훨씬 더 풍부한 사용자 경험을 제공합니다.

1단계: 데이터 소스 정의

다양한 유형의 데이터를 다루기 위해 공통 인터페이스 또는 추상 클래스를 정의합니다. 각 데이터 유형은 이 인터페이스를 구현하여 자신만의 렌더링 방식을 제공합니다.

abstract class ListItem {
  Widget buildTitle(BuildContext context);
  Widget buildSubtitle(BuildContext context);
}

class HeadingItem implements ListItem {
  final String heading;
  HeadingItem(this.heading);

  @override
  Widget buildTitle(BuildContext context) {
    return Text(
      heading,
      style: Theme.of(context).textTheme.headlineSmall,
    );
  }

  @override
  Widget buildSubtitle(BuildContext context) => const SizedBox.shrink();
}

class MessageItem implements ListItem {
  final String sender;
  final String body;
  MessageItem(this.sender, this.body);

  @override
  Widget buildTitle(BuildContext context) => Text(sender);

  @override
  Widget buildSubtitle(BuildContext context) => Text(body);
}

2단계: 데이터 리스트 생성

리스트에 표시할 데이터를 정의합니다. 예를 들어, 헤더와 메시지를 혼합한 데이터를 생성합니다.

final items = List<ListItem>.generate(
  100,
  (i) => i % 6 == 0
      ? HeadingItem('Heading $i')
      : MessageItem('Sender $i', 'Message body $i'),
);

위 코드는 6번째마다 헤더를 삽입하고 나머지 항목은 메시지로 채웁니다.

3단계: ListView.builder로 데이터 표시

ListView.builder를 사용하여 데이터를 효율적으로 렌더링합니다. 각 아이템의 유형에 따라 적절한 위젯을 반환합니다.

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    final item = items[index];
    return ListTile(
      title: item.buildTitle(context),
      subtitle: item.buildSubtitle(context),
    );
  },
);

주요 구현 포인트

1. 타입에 따라 위젯 렌더링

itemBuilder 내부에서 item의 타입에 따라 HeadingItemMessageItem을 구분합니다. 이를 통해 각 아이템 유형에 적합한 UI를 구성합니다.

2. 동적 리스트 생성

ListView.builder는 필요한 아이템만 생성하므로, 대규모 데이터를 다룰 때도 성능이 우수합니다.

3. 스타일링

Theme.of(context).textTheme을 사용해 헤더와 메시지의 텍스트 스타일을 쉽게 관리할 수 있습니다.

혼합형 리스트의 활용 사례

  1. 채팅 앱:
    • 날짜별 섹션 구분과 메시지 표시.
  2. 상품 카탈로그:
    • 섹션별 카테고리 제목과 상품 리스트.
  3. 뉴스 피드:
    • 기사 헤드라인과 요약 정보.

결론

Flutter에서 혼합형 리스트를 구현하면 다양한 유형의 데이터를 효율적으로 표시할 수 있습니다. ListView.builder와 사용자 정의 클래스를 활용하여 유연하고 확장 가능한 UI를 만들어 보세요. 이는 채팅 앱, 뉴스 앱, 전자상거래 플랫폼 등 다양한 애플리케이션에서 중요한 역할을 합니다.

 

import 'package:flutter/material.dart';

void main() {
  runApp(
    MyApp(
      items: List<ListItem>.generate(
        1000,
        (i) => i % 6 == 0
            ? HeadingItem('Heading $i')
            : MessageItem('Sender $i', 'Message body $i'),
      ),
    ),
  );
}

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

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

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

    return MaterialApp(
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(title),
        ),
        body: ListView.builder(
          // Let the ListView know how many items it needs to build.
          itemCount: items.length,
          // Provide a builder function. This is where the magic happens.
          // Convert each item into a widget based on the type of item it is.
          itemBuilder: (context, index) {
            final item = items[index];

            return ListTile(
              title: item.buildTitle(context),
              subtitle: item.buildSubtitle(context),
            );
          },
        ),
      ),
    );
  }
}

/// The base class for the different types of items the list can contain.
abstract class ListItem {
  /// The title line to show in a list item.
  Widget buildTitle(BuildContext context);

  /// The subtitle line, if any, to show in a list item.
  Widget buildSubtitle(BuildContext context);
}

/// A ListItem that contains data to display a heading.
class HeadingItem implements ListItem {
  final String heading;

  HeadingItem(this.heading);

  @override
  Widget buildTitle(BuildContext context) {
    return Text(
      heading,
      style: Theme.of(context).textTheme.headlineSmall,
    );
  }

  @override
  Widget buildSubtitle(BuildContext context) => const SizedBox.shrink();
}

/// A ListItem that contains data to display a message.
class MessageItem implements ListItem {
  final String sender;
  final String body;

  MessageItem(this.sender, this.body);

  @override
  Widget buildTitle(BuildContext context) => Text(sender);

  @override
  Widget buildSubtitle(BuildContext context) => Text(body);
}