ListView

2024. 6. 17. 13:00Flutter

 

✅ 학습 목표

 

✅ ListView란?

 

 

✅ Widget

 
 
✅ vertical
import 'package:flutter/material.dart';

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

  // 1. 대량의 데이터를 관리할 수 있는 리스트 필요!
  // 2. 대량의 데이터를 띄울 디자인 필요

  @override
  Widget build(BuildContext context) {
    // List<int> list = [1, 2, 3, 4]; -> 기존의 방법
    var intList = List<int>.generate(50, (i) => i++); // 0 ~ 49 => 50개!

    return Scaffold(
      body: SafeArea(
        child: ListView.builder(

          // 생성하지 않으면 화면 설계시 오류가 발생!
          // 아이템의 갯수를 지정하기 위하여 생성된 리스트의 길이를 활용한다!
          itemCount: intList.length,

          // ListView의 필수 요소!
          itemBuilder: (context, index) => Container(
            height: 50,
            margin: EdgeInsets.all(4),
            color: Colors.pink[100],
            child: Text(
              '${intList[index] + 1}', // 1부터 실행하고 싶으면 + 1
              style: TextStyle(fontSize: 25, color: Colors.pink),
            ),
          ),
        ),
      ),
    );
  }
}

 

 

✅ horizontal

import 'package:flutter/material.dart';

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

  // 1. 리스트 뷰의 데이터 생성
  // 2. 리스트 뷰 디자인 생성
  //  - 필수요소 체크하기!
  //  - 화면 실행시 발생되는 오류 해결하기!

  @override
  Widget build(BuildContext context) {
    var intList2 = List<int>.generate(
        50, (i) => i++); // 같은 폴더 안에 동일한 변수명을 가지면 충돌이 일어날 확률이 높기 때문에 변경!

    return Scaffold(
      body: SafeArea(
        child: ListView.builder(
          // 리스트뷰의 기본 형태는 Vertical!
          // 형태를 바꾸기 위한 기능 -> scrollDirection -> Axis.horizontal,
          scrollDirection: Axis.horizontal,

          itemCount: intList2.length,
          itemBuilder: (context, index) => Center(
            child: Container(
              color: Colors.pink[100],
              margin: EdgeInsets.all(4),
              child: Text(
                '${intList2[index] + 1}', // 1부터 실행하고 싶으면 + 1
                style: TextStyle(fontSize: 25, color: Colors.pink),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

✅ gird

import 'package:flutter/material.dart';

// Grid를 생성하는 방식은 두가지의 방식이 있다!
//  - count 방식 / extent 방식

// 1. Count 방식
class ExGridCount extends StatelessWidget {
  const ExGridCount({super.key});

  @override
  Widget build(BuildContext context) {
    var intList3 = List<int>.generate(30, (i) => i++);

    return Scaffold(
      body: SafeArea(
        child: GridView.builder(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            // crossAxisCount -> 하나의 축에 몇 개의 데이터를 넣을지 지정하는 요소!
            crossAxisCount: 3,
            childAspectRatio: 1 / 2, // 가로/세로 -> 비율
            crossAxisSpacing: 20, // 그리드와 그리드 사이의 간격 조절!
            mainAxisSpacing: 20, // 축과 축 사이의 간격 조절!
          ),
          itemCount: intList3.length,
          itemBuilder: (context, index) => Card(
            child: Container(
              color: Colors.amber[100],
              child: Text(
                '${intList3[index]} 번째',
                style: TextStyle(
                  color: Colors.amber,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

// 2. Extent 방식
class ExGridExtent extends StatelessWidget {
  const ExGridExtent({super.key});

  @override
  Widget build(BuildContext context) {

    var intList4 = List<int>.generate(30, (i) => i++);

    return Scaffold(
      body: SafeArea(
        child: GridView.builder(
            // 디바이스의 너비를 기준으로 배치하는 기능!
            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
              // 디바이스 너비 / maxCrossAxisExtent(지정값) + 1 만큼 그리드를 배치
              maxCrossAxisExtent: 100,
            ),
            itemCount: intList4.length,
            itemBuilder: (context, index) =>
        Card(child: Container(color: Colors.teal[100],
        child: Text('${intList4[index]} 번째', style: TextStyle(color: Colors.teal),),),)),
      ),
    );
  }
}

 

✅ ListView 실습

  assets:
    - images/
import 'package:flutter/material.dart';

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

  @override
  State<ExRyan> createState() => _ExRyanState();
}

// 1. 데이터를 관리하기 위한 리스트 생성! -> 이미지 / 텍스트

var imgList = [
  'images/ryan1.jpg',
  'images/ryan2.png',
  'images/ryan3.jpg',
  'images/ryan4.png',
  'images/ryan5.png',
  'images/ryan6.jpg'
];

var txtList = ['리틀 라이언', '반짝 라이언', '하트 라이언', '춘식이와의 만남', '룸메는 춘식이', '좋아요 라이언'];

class _ExRyanState extends State<ExRyan> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 액션을 감지하는 버튼 생성
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 버튼이 눌리면 리스트가 추가되는 기능 설계!
          setState(() {
            imgList.add('images/t1.jpg');
            txtList.add('우승 가자!');
          });
        },
        child: Icon(
          Icons.add,
          color: Colors.black,
        ),
        backgroundColor: Colors.yellow,
      ),

      // 2. ListView 생성 작업 시작!
      body: SafeArea(
        child: ListView.builder(
          itemCount: imgList.length,
          itemBuilder: (context, index) => Card(
            child: Container(
              color: Colors.black,
              child: Row(
                children: [
                  // 각각의 이미지의 크기를 일정한 비율로 맞추기 위한 Expanded 위젯 사용!
                  Expanded(child: Image.asset(imgList[index])),
                  Expanded(
                    child: Column(
                      children: [
                        Text(
                          '${txtList[index]}',
                          style: TextStyle(color: Colors.white),
                        ),
                        Text(
                          '${index + 1} 번째 라이언',
                          style: TextStyle(color: Colors.white),
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

✅ Dialog

import 'package:flutter/material.dart';
import 'package:flutter0617/ryan/ryanDetail.dart';

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

  @override
  State<ExRyan> createState() => _ExRyanState();
}

class _ExRyanState extends State<ExRyan> {
  // 1. 데이터를 관리하기 위한 리스트 생성! -> 이미지 / 텍스트

  var imgList = [
    'images/ryan1.jpg',
    'images/ryan2.png',
    'images/ryan3.jpg',
    'images/ryan4.png',
    'images/ryan5.png',
    'images/ryan6.jpg'
  ];

  var txtList = [
    '리틀 라이언',
    '반짝 라이언',
    '하트 라이언',
    '춘식이와의 만남',
    '룸메는 춘식이',
    '좋아요 라이언'
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 액션을 감지하는 버튼 생성
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 버튼이 눌리면 리스트가 추가되는 기능 설계!
          setState(() {
            imgList.add('images/t1.jpg');
            txtList.add('우승 가자!');
          });
        },
        child: Icon(
          Icons.add,
          color: Colors.black,
        ),
        backgroundColor: Colors.yellow,
      ),

      // 2. ListView 생성 작업 시작!
      body: SafeArea(
        child: ListView.builder(
          itemCount: imgList.length,
          itemBuilder: (context, index) => GestureDetector(
            // 리스트뷰에 있는 각각의 항목이 제스처를 감지하도록 감싼다!
            onTap: () {
              // print('${txtList[index]}');
            
              // 항목이 선택되었을 경우 팝업창 띄우기!
              showPop(imgList[index], txtList[index], index);
            },
            child: Card(
              child: Container(
                color: Colors.black,
                child: Row(
                  children: [
                    // 각각의 이미지의 크기를 일정한 비율로 맞추기 위한 Expanded 위젯 사용!
                    Expanded(child: Image.asset(imgList[index])),
                    Expanded(
                      child: Column(
                        children: [
                          Text(
                            '${txtList[index]}',
                            style: TextStyle(color: Colors.white),
                          ),
                          Text(
                            '${index + 1} 번째 라이언',
                            style: TextStyle(color: Colors.white),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  void showPop(image, name, index) {
    showDialog(
        context: context,
        builder: (context) {
          return Dialog(
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              height: 380,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                color: Colors.yellow,
              ),
              child: Column(
                children: [
                  SizedBox(
                    height: 32,
                  ),
                  ClipRRect(
                    borderRadius: BorderRadius.circular(10),
                    child: Image.asset(
                      image,
                      width: 200,
                      height: 200,
                    ),
                  ),
                  SizedBox(
                    height: 10,
                  ),
                  Text(
                    name,
                    style: TextStyle(
                        fontSize: 25,
                        fontWeight: FontWeight.bold,
                        color: Colors.orange),
                  ),
                  Padding(
                    padding: EdgeInsets.all(8),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        ElevatedButton.icon(
                          onPressed: () {},
                          icon: Icon(
                            Icons.close,
                            color: Colors.yellow,
                          ),
                          label: Text(
                            '삭제하기',
                            style: TextStyle(color: Colors.yellow),
                          ),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.yellow[800],
                          ),
                        ),
                        SizedBox(
                          width: 10,
                        ),
                        ElevatedButton.icon(
                          onPressed: () {},
                          icon: Icon(
                            Icons.close,
                            color: Colors.yellow,
                          ),
                          label: Text(
                            'close',
                            style: TextStyle(color: Colors.yellow),
                          ),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.yellow[800],
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          );
        }); // {} : context로부터 기능을 만들겠다
  }
}

 

✅ Detail 페이지 생성

import 'package:flutter/material.dart';

import 'package:flutter0617/ryan/ryanDetail.dart';
import 'package:flutter0617/ryan/ryanModel.dart';

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

  @override
  State<ExRyan> createState() => _ExRyanState();
}

class _ExRyanState extends State<ExRyan> {
  // 1. 데이터를 관리하기 위한 리스트 생성! -> 이미지 / 텍스트

  var imgList = [
    'images/ryan1.jpg',
    'images/ryan2.png',
    'images/ryan3.jpg',
    'images/ryan4.png',
    'images/ryan5.png',
    'images/ryan6.jpg'
  ];

  var txtList = [
    '리틀 라이언',
    '반짝 라이언',
    '하트 라이언',
    '춘식이와의 만남',
    '룸메는 춘식이',
    '좋아요 라이언'
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 액션을 감지하는 버튼 생성
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 버튼이 눌리면 리스트가 추가되는 기능 설계!
          setState(() {
            imgList.add('images/t1.jpg');
            txtList.add('우승 가자!');
          });
        },
        child: Icon(
          Icons.add,
          color: Colors.black,
        ),
        backgroundColor: Colors.yellow,
      ),

      // 2. ListView 생성 작업 시작!
      body: SafeArea(
        child: ListView.builder(
          itemCount: imgList.length,
          itemBuilder: (context, index) => GestureDetector(
            // 리스트뷰에 있는 각각의 항목이 제스처를 감지하도록 감싼다!
            // onTap: () {
            //   // print('${txtList[index]}');
            //
            //   // 항목이 선택되었을 경우 팝업창 띄우기!
            //   showPop(imgList[index], txtList[index], index);
            // },

            onLongPress: () {
              // 꾹 누르기
              // 디테일 페이지로 넘어가는 기능 만들기! -> Route 사용
              // 1. onLongPress 구조 만들기
              // 2. 디테일 페이지 생성
              // 3. 데이터의 객체를 생성하여 사용! -> 대량의 데이터를 사용할 때 편리하다!

              // ryan 객체를 사용하여 데이터들을 하나의 묶음으로 만들기!
              Ryan ryan =
                  Ryan(imgList[index], txtList[index], '${index}번째 라이언');

              Navigator.push(
                  context,
                  MaterialPageRoute(
                      builder: (_) =>
                          // RyanDetail(
                          //   title: txtList[index],
                          //   img: imgList[index],
                          //   numberName: '${index}번째 라이언',
                          // )
                          RyanDetail(
                            r: ryan,
                          )));
            },
            child: Card(
              child: Container(
                color: Colors.black,
                child: Row(
                  children: [
                    // 각각의 이미지의 크기를 일정한 비율로 맞추기 위한 Expanded 위젯 사용!
                    Expanded(child: Image.asset(imgList[index])),
                    Expanded(
                      child: Column(
                        children: [
                          Text(
                            '${txtList[index]}',
                            style: TextStyle(color: Colors.white),
                          ),
                          Text(
                            '${index + 1} 번째 라이언',
                            style: TextStyle(color: Colors.white),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  void showPop(image, name, index) {
    showDialog(
        context: context,
        builder: (context) {
          return Dialog(
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              height: 380,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                color: Colors.yellow,
              ),
              child: Column(
                children: [
                  SizedBox(
                    height: 32,
                  ),
                  ClipRRect(
                    borderRadius: BorderRadius.circular(10),
                    child: Image.asset(
                      image,
                      width: 200,
                      height: 200,
                    ),
                  ),
                  SizedBox(
                    height: 10,
                  ),
                  Text(
                    name,
                    style: TextStyle(
                        fontSize: 25,
                        fontWeight: FontWeight.bold,
                        color: Colors.orange),
                  ),
                  Padding(
                    padding: EdgeInsets.all(8),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        ElevatedButton.icon(
                          onPressed: () {},
                          icon: Icon(
                            Icons.close,
                            color: Colors.yellow,
                          ),
                          label: Text(
                            '삭제하기',
                            style: TextStyle(color: Colors.yellow),
                          ),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.yellow[800],
                          ),
                        ),
                        SizedBox(
                          width: 10,
                        ),
                        ElevatedButton.icon(
                          onPressed: () {},
                          icon: Icon(
                            Icons.close,
                            color: Colors.yellow,
                          ),
                          label: Text(
                            'close',
                            style: TextStyle(color: Colors.yellow),
                          ),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.yellow[800],
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          );
        }); // {} : context로부터 기능을 만들겠다
  }
}
import 'package:flutter/material.dart';

import 'package:flutter0617/ryan/ryanModel.dart';

class RyanDetail extends StatelessWidget {
  // const RyanDetail(
  //     {super.key,
  //     required this.title,
  //     required this.img,
  //     required this.numberName});

  // ryan listview에서 ryanDetail로 넘어올 때 가지고 와야하는 데이터?
  const RyanDetail({required this.r});

  // final String title;
  // final String img;
  // final String numberName;

  final Ryan r;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          r.numberName,
          style: TextStyle(
            color: Colors.orange
          ),
        ),
        backgroundColor: Colors.yellow[400],
      ), // 나중에 실제 들어온 데이터로 수정!
      body: Container(
        color: Colors.yellow[400],
        child: Center(
          child: Column(
            children: [
              Image.asset(r.img),
              // 나중에 실제 들어온 데이터로 수정!
              SizedBox(
                width: 10,
              ),
              Text(
                r.title,
                style: TextStyle(fontSize: 24, color: Colors.orange),
              )
              // 나중에 실제 들어온 데이터로 수정!
            ],
          ),
        ),
      ),
    );
  }
}
// 객체(=클래스)가 가지고 있어야 하는 내용을 정리하는 파일!

class Ryan {
  String img;
  String title;
  String numberName;

  // 무조건 3개의 데이터를 초기화하는 생성자 메소드 생성
  Ryan(this.img, this.title, this.numberName);

}

'Flutter' 카테고리의 다른 글

Future  (0) 2024.06.18
BottomPage  (0) 2024.06.17
ex33. Route  (0) 2024.06.14
ex32. Navigator  (0) 2024.06.14
ex31. Onboarding  (0) 2024.06.13