[Flutter] ListView.builder

비나·2022년 11월 7일
0

Flutter

목록 보기
12/16
post-thumbnail

📌 ListView vs. ListView.builder

일단 ListView.builder를 알기 위해 ListView와의 다른점을 알아야 한다.

우선 이 둘의 공통점은 스크롤이 가능한 배열형 위젯이라는 점이다.
반면 ListView는 리스트 뷰 안에 모든 child를 생성해서 보여주고,
ListView.builder는 그때그때 필요한 만큼만 데이터를 저장소나 서버에서 불러온다는 것이 차이점이다.
즉, 데이터가 엄청 많은 경우 그거를 한번에 싹 다 리스트 뷰로 가져오면 시간도 오래걸린다.
이걸 리스트 뷰 빌더로 하면 좋다는 말이다.

그래서 이번에 들은 코딩셰프 Flutter 순한맛 시즌2 강의에서는 ListView.builder로 리스트를 만들고 Dialog 팝업창을 띄우는 것을 공부했다.




📌 리스트 만들기

그래서 오늘도 기본적인 예제의 구성을 보면 아래와 같다.

그럼 이제 이 구성을 바탕으로 작성한 예제를 보자.

✅ 예제 코드

오늘 예제는 main.dart 파일 하나에 작성하였는데,
좀 긴거 같아서 나누어서 살펴보자.

🌟 MyApp()

MyApp 위젯의 경우 MaterialApp을 리턴하고,
MaterialApp에서 home argument의 값으로 ListViewPage 위젯을 준다.

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // ignore: prefer_const_constructors
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const ListViewPage(),
    );
  }
}

🌟 ListViewPage()

이제 ListViewPage 위젯의 코드를 보면 다음과 같다.
꾸며주는 스타일 부분은 제외하고 간략하게 보면 아래와 같이 구성된다.

class ListViewPage extends StatefulWidget {
  const ListViewPage({super.key});

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var titleList = [
    'person01',
    'person02',
    ...	// 리스트에 보여질 리스트 각각의 title 작성
  ];
  var imgList = [
    'assets/man.png',
    'assets/man.png',
    ...	// 리스트에 보여질 이미지들의 경로들을 작성
  ];
  var description = [
    'Lorem ipsum dolor sit amet',
    'Lorem ipsum dolor sit amet',
    ...	// 리스트에 보여지는 description들을 작성
  ];

  // popup 띄우기 위한 메서드 구현
  void showPopup(context, title, img, description) {
    showDialog(
        context: context,
        builder: (context) {
          return Dialog(
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              child: Column(
                children: [
                ...	// 팝업창의 디자인적 스타일 요소 작성 
                  // ElevatedButton으로 팝업창 닫는 버튼을 만들어 줌 
                  ElevatedButton.icon(
                    onPressed: () {
                      Navigator.pop(context);	// 팝업창을 pop해서 스택에서 없애줌
                    },
                    icon: const Icon(Icons.close),
                    label: const Text('close'),
                  ),
                ],
              ),
            ),
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    // 반응형 웹 구현을 위해 MediaQuery class 사용
    // 끝에 숫자는 화면을 차지하는 퍼센트를 의미 : 0.6을 곱하면 화면의 60퍼센트 차지
    double width = MediaQuery.of(context).size.width * 0.8;
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'List View',
          style: TextStyle(color: Colors.white),
        ),
        backgroundColor: Color.fromARGB(255, 255, 200, 0),
        elevation: 0.0,
      ),
      body: ListView.builder(
        itemCount: titleList.length,
        itemBuilder: (context, index) {
          return GestureDetector( // GestureDetector 대신 InkWell 위젯을 사용해도 됨(애니메이션 효과가 좀 다름)
            onTap: () {
              // debugPrint(titleList[index]);
              showPopup(context, titleList[index], imgList[index],
                  description[index]);
            },
            child: Card(
              child: Row(
                children: [
                ...	// card로 보여질 리스트 부분의 스타일 작성
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

✅ 예제 전체 코드

그래서 강의 예제를 따라한 전체 코드는 다음과 같다.

import 'dart:ffi';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // ignore: prefer_const_constructors
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const ListViewPage(),
    );
  }
}

class ListViewPage extends StatefulWidget {
  const ListViewPage({super.key});

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var titleList = [
    'person01',
    'person02',
    'person03',
    'person04',
    'person05',
    'person06',
    'person07',
    'person08',
    'person09',
    'person10',
  ];
  var imgList = [
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
    'assets/man.png',
  ];
  var description = [
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
    'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
  ];

  // popup 띄우기 위한 메서드 구현
  void showPopup(context, title, img, description) {
    showDialog(
        context: context,
        builder: (context) {
          return Dialog(
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              height: 380,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(20),
                border: Border.all(
                  width: 5,
                  color: const Color.fromARGB(255, 255, 200, 0),
                ),
                color: Color.fromARGB(255, 255, 238, 175),
              ),
              child: Column(
                children: [
                  ClipRRect(  // 모서리 둥글게 만들때 사용 
                    borderRadius: BorderRadius.circular(10),
                    child: Image.asset(
                      img,
                      width: 200,
                      height: 200,
                    ),
                  ),
                  const SizedBox(
                    height: 10,
                  ),
                  Text(
                    title,
                    style: const TextStyle(
                        fontSize: 25,
                        fontWeight: FontWeight.bold,
                        color: Colors.grey),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Text(
                      description,
                      maxLines: 3, // description 데이터가 최대 몇 줄 보여지게 할 지
                      style: const TextStyle(
                          fontSize: 15, color: Color.fromARGB(255, 96, 96, 96)),
                      textAlign: TextAlign.center,
                    ),
                  ),
                  const SizedBox(
                    height: 20,
                  ),
                  ElevatedButton.icon(
                    onPressed: () {
                      Navigator.pop(context);
                    },
                    icon: const Icon(Icons.close),
                    label: const Text('close'),
                  ),
                ],
              ),
            ),
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    // 반응형 웹 구현을 위해 MediaQuery class 사용
    // 끝에 숫자는 화면을 차지하는 퍼센트를 의미 : 0.6을 곱하면 화면의 60퍼센트 차지
    double width = MediaQuery.of(context).size.width * 0.8;
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'List View',
          style: TextStyle(color: Colors.white),
        ),
        backgroundColor: Color.fromARGB(255, 255, 200, 0),
        elevation: 0.0,
      ),
      body: ListView.builder(
        itemCount: titleList.length,
        itemBuilder: (context, index) {
          return GestureDetector( // GestureDetector 대신 InkWell 위젯을 사용해도 됨 
            onTap: () {
              // debugPrint(titleList[index]);
              showPopup(context, titleList[index], imgList[index],
                  description[index]);
            },
            child: Card(
              child: Row(
                children: [
                  // 공간의 확보를 위해서는 SizedBox가 Container 보다 효율적임
                  SizedBox(
                    width: 100,
                    height: 100,
                    child: Image.asset(imgList[index]),
                  ),
                  Padding(
                    padding: EdgeInsets.all(10),
                    child: Column(
                      children: [
                        Text(
                          titleList[index],
                          style: const TextStyle(
                            fontSize: 22,
                            fontWeight: FontWeight.bold,
                            color: Colors.grey,
                          ),
                        ),
                        const SizedBox(
                          height: 10,
                        ),
                        SizedBox(
                            width: width,
                            child: Text(
                              description[index],
                              style: const TextStyle(
                                fontSize: 15,
                                color: Color.fromARGB(255, 81, 81, 81),
                              ),
                            )),
                        const SizedBox(
                          height: 10,
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

✅ 화면 결과

이에 대한 결과 화면은 아래와 같이 나온다.
리스트가 잘 생성되는 것을 볼 수 있고, 리스트의 각 카드를 선택하면 두번째 사진과 같은 팝업창이 나타난다.




📌 마무리

오늘은 ListView.Builder를 이용하여 리스트를 구현하고,
Dialog 위젯을 사용하여 팝업창을 구현하는 부분을 정리했다.
이제 코딩셰프 Flutter 순한맛 시즌2 강의도 거의 마친 상태이다.
그래서 좀 간단한 어플을 만들면서 다음 조금 매운맛 강좌도 들으면서 공부할 예정이다.
그래도 이제 대략적인 흐름과 구현 방법이 보이기 시작해서 재미가 꽤 있다.
오늘은 여기까지 쓰고 이제 포스팅 마무리 🌱✨




참고
- Flutter
- Dart packages
- 코딩셰프 Flutter 순한맛 시즌2
profile
아자아자 코딩라이푸 ٩(๑❛ʚ❛๑)۶

0개의 댓글