본문 바로가기
공부기록/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
반응형