본문 바로가기
공부기록/Flutter

[Flutter] ListView 재사용하기(재사용 가능한 Widget 만들기)

by 책읽는 개발자 ami 2023. 7. 5.
728x90
반응형

 

 

Flutter에서 ListView 재사용하기

똑같은 형태의 Widget을 구현할 때는 Widget을 재사용 할 수 있다.

bottomNavigatior로 탭하여 화면을 바꾸는 것은 아래 링크에서 확인해주세요. 
2023.07.05 - [공부기록/Flutter] - [Flutter] AppBar, BottomNavigator 구현(상단,하단은 고정 & body만 바뀌도록)

이번 글에서는 Widget을 재사용하는 방법에 대해 알아보겠습니다.

verticalList.dart

VerticalListWidget은 StatefulWidget이다. 이 위젯은 수직 스크롤이 가능한 리스트를 생성하며, 각 아이템은 제목, 기간, 장소/날짜 정보, 그리고 이미지로 구성된다.

initState 메서드에서는 widget.itemList을 기반으로 _isAlarmActiveList를 초기화한다. itemList의 각 아이템의 alarmyn 값이 'Y'인 경우 _isAlarmActiveList의 해당 인덱스를 true로 설정한다.

build 메서드에서는 ListView.builder를 사용하여 아이템을 생성. 각 아이템은 Row 위젯으로 구성되며, 좌측에는 텍스트 정보를, 우측에는 이미지를 표시한다. 알림 아이콘은 GestureDetector를 사용하여 토글 기능을 구현하였으며, _isAlarmActiveList의 상태에 따라 아이콘의 색상이 변경된다.

VerticalListWidget은 itemList 속성을 받아 수직 리스트를 생성하며, 알림 아이콘을 토글할 수 있는 기능을 제공한다.

import 'package:flutter/material.dart';

class VerticalListWidget extends StatefulWidget {
  final List<Map<String, String>> itemList;

  VerticalListWidget({required this.itemList});

  @override
  _VerticalListWidgetState createState() => _VerticalListWidgetState();
}

class _VerticalListWidgetState extends State<VerticalListWidget> {
  late List<Map<String, String>> itemList;
  late List<bool> _isAlarmActiveList;

  @override
  void initState() {
    super.initState();
    itemList = widget.itemList; // Access widget property in initState
    _isAlarmActiveList = List<bool>.filled(itemList.length, false);
    // Initialize alarm toggle state based on alarmyn value
    for (int i = 0; i < itemList.length; i++) {
      if (itemList[i]['alarmyn'] == 'Y') {
        _isAlarmActiveList[i] = true;
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: itemList.length,
      itemBuilder: (context, index) {
        //bool isAlarmActive = itemList[index]['alarmyn'] == 'Y';
        bool isAlarmActive = _isAlarmActiveList[index];
        return Container(
          padding: EdgeInsets.all(16.0),
          child: Row(
            children: [
              Expanded(
                flex: 8,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      itemList[index]['title'].toString(),
                      style: TextStyle(
                        fontSize: 24.0,
                        color: Colors.black,
                      ),
                    ),
                    SizedBox(height: 8.0),
                    Text(
                      itemList[index]['period'] == null
                          ? ''
                          : itemList[index]['period'].toString(),
                      style: TextStyle(
                        fontSize: 16.0,
                        color: Colors.grey,
                      ),
                    ),
                    Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text(
                            itemList[index]['location'] == null
                                ? itemList[index]['dt'].toString()
                                : itemList[index]['location'].toString(),
                            style: TextStyle(
                              fontSize: 16.0,
                              color: Colors.grey.shade700,
                            ),
                          ),
                          Visibility(
                            visible: itemList[index]['location'] ==
                                null, //티켓오픈소식일때만 보이도록
                            child: GestureDetector(
                              onTap: () {
                                setState(() {
                                  //isAlarmActive = !isAlarmActive;
                                  _isAlarmActiveList[index] =
                                      !_isAlarmActiveList[index];
                                });
                              },
                              child: Padding(
                                padding: EdgeInsets.only(right: 18.0),
                                child: Icon(
                                  isAlarmActive
                                      ? Icons.notifications_active
                                      : Icons.notifications_none_outlined,
                                  color: isAlarmActive
                                      ? Colors.yellow
                                      : Colors.grey.shade700,
                                ),
                              ),
                            ),
                          )
                        ]),
                  ],
                ),
              ),
              Expanded(
                flex: 2,
                child: Image.network(
                  itemList[index]['url'].toString(),
                  fit: BoxFit.cover,
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

tab1.dart

Tab1Page는 StatelessWidget이다. 이 페이지는 탭 1 리스트를 보여주는 화면이다.

itmList는 맵 형식으로 이루어진 아이템 리스트입니다. 각 아이템은 제목, 기간, 장소, 이미지 URL로 구성되어 있다.

build 메서드에서는 Column 위젯을 사용하여 화면을 구성한다. 제목을 나타내는 텍스트와 수직 스크롤 가능한 리스트를 포함하고 있다. 리스트는 VerticalListWidget 위젯을 사용하여 생성되며, itemList 속성에 itmList를 전달하여 아이템 리스트를 표시한다.

이 페이지는 '탭1 리스트'라는 제목과 수직 스크롤 가능한 아이템 리스트를 가진 화면을 나타낸다.

import 'package:flutter/material.dart';
import 'package:musicalapp/verticalList.dart';

class Tab1Page extends StatelessWidget {
  List<Map<String, String>> itmList = [
    {
      'title': 'Title 1',
      'period': '2023-06-19~2023-06-30',
      'location': 'Loc 1',
      'url':
          'https://cdn.pixabay.com/photo/2023/05/19/15/40/man-8004816_1280.jpg'
    },
    {
      'title': 'Title 2',
      'period': '2023-06-19~2023-06-30',
      'location': 'Loc 2',
      'url':
          'https://cdn.pixabay.com/photo/2023/06/17/22/51/shoes-8070908_640.jpg'
    },
    {
      'title': 'Title 3',
      'period': '2023-06-19~2023-06-30',
      'location': 'Loc 3',
      'url':
          'https://cdn.pixabay.com/photo/2023/05/30/17/20/woman-8029209_640.jpg'
    }
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              '탭1 리스트',
              style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
            ),
          ),
          Expanded(child: VerticalListWidget(itemList: itmList)),
        ],
      ),
    );
  }
}

tab2.dart

tab1.dart와 동일한 구조를 가졌다. itmList 구조만 다르다.

import 'package:flutter/material.dart';
import 'package:musicalapp/verticalList.dart';

class Tab2Page extends StatelessWidget {
  List<Map<String, String>> itmList = [
    {
      'title': 'Title 1',
      'dt': '2023-06-19~2023-06-30',
      'alarmyn': 'Y',
      'url':
          'https://cdn.pixabay.com/photo/2016/02/21/12/09/heart-1213475_1280.jpg'
    },
    {
      'title': 'Title 2',
      'dt': '2023-06-19~2023-06-30',
      'alarmyn': 'Y',
      'url':
          'https://cdn.pixabay.com/photo/2019/06/20/09/26/underwater-4286600_640.jpg'
    },
    {
      'title': 'Title 3',
      'dt': '2023-06-19~2023-06-30',
      'alarmyn': 'N',
      'url':
          'https://cdn.pixabay.com/photo/2017/09/02/15/10/lighthouse-2707528_640.jpg'
    }
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              '탭2 리스트',
              style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
            ),
          ),
          Expanded(child: VerticalListWidget(itemList: itmList)),
        ],
      ),
    );
  }
}

자주 사용하는 ListView 형태를 미리 구현해두고, 데이터만 넘겨주는 방식으로 재사용을 할 수 있다.

(정식으로 공부를 해서 작성하는 코드가 아니므로 더 좋은 방법이 있을 수 도 있습니다. 더 효율적인 방법이 있다면 댓글로 알려주시면 감사드리겠습니다!)

728x90
반응형