본문 바로가기
코딩생초보를 위한 플러터 빠르게 한바퀴

3.component

by 타이싸란 2023. 8. 3.

3회차 세부 과정 목차

 

 지난번 강의 숙제풀이

목차로돌아가기

 

더보기

저번 시간의 숙제 내용입니다.

프로젝트의 구조입니다.

main.dart 파일이 있고,

pages 폴더안에 MainPage.dart , ProductDetailPage.dat 파일이 있습니다.

main.dart 에서 GetMaterialApp 이라고 해줬습니다.

 

MainPage.dart 파일의 내용입니다.

 

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:homework_1/pages/ProductDetailPage.dart';

class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("사무실쇼핑몰"),centerTitle: true,backgroundColor: Colors.orangeAccent,),
      body: Container(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: Column(
          children: [
            SizedBox(height: 50,),
            Row(
              children: [
                Expanded(
                  child: GestureDetector(
                    onTap: (){
                      Get.to(()=>ProductDetailPage(productName: "의자", imagePath: "https://m.comfpro.co.kr/web/product/medium/202111/ab99bbc3f5c49160158c29d07ce660bf.jpg", price: "3,000 원"));
                    },
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 2/1,
                          child: Container(
                            width: double.infinity,
                            child: Image.network('https://m.comfpro.co.kr/web/product/medium/202111/ab99bbc3f5c49160158c29d07ce660bf.jpg',fit: BoxFit.cover,),
                          ),
                        ),
                        SizedBox(height: 20,),
                        Text("의자"),
                        Text("3,000 원")

                      ],
                    ),
                  ),
                ),
                SizedBox(width: 10,),
                Expanded(
                  child: GestureDetector(
                    onTap: (){
                      Get.to(()=>ProductDetailPage(productName: "냉장고", imagePath: "https://t1.daumcdn.net/cfile/tistory/99E36F345E9E595031", price: "5,000 원"));
                    },
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 2/1,
                          child: Container(
                            width: double.infinity,
                            child: Image.network('https://t1.daumcdn.net/cfile/tistory/99E36F345E9E595031',fit: BoxFit.cover),
                          ),
                        ),
                        SizedBox(height: 20,),
                        Text("냉장고"),
                        Text("5,000 원")

                      ],
                    ),
                  ),
                ),
              ],
            ),
            SizedBox(height: 50,),
            Row(
              children: [
                Expanded(
                  child: GestureDetector(
                    onTap: (){
                      Get.to(()=>ProductDetailPage(productName: "자동차", imagePath: "https://img.hankyung.com/photo/202203/0ad0cd5a2df34e36cd91fd08dcbb46c2.jpg", price: "5,000 원"));
                    },
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 2/1,
                          child: Container(
                            width: double.infinity,
                            child: Image.network('https://img.hankyung.com/photo/202203/0ad0cd5a2df34e36cd91fd08dcbb46c2.jpg',fit: BoxFit.cover,),
                          ),
                        ),
                        SizedBox(height: 20,),
                        Text("자동차"),
                        Text("6,000 원"),

                      ],
                    ),
                  ),
                ),
                SizedBox(width: 10,),
                Expanded(
                  child: GestureDetector(
                    onTap: (){
                      Get.to(()=>ProductDetailPage(productName: "냉장고", imagePath: "https://reviewpro.co.kr/wp-content/uploads/2020/08/%EC%97%90%EC%96%B4%EC%BB%A8%EB%A9%94%EC%9D%B8.jpg", price: "7,000 원"));
                    },
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 2/1,
                          child: Container(
                            width: double.infinity,
                            child: Image.network('https://reviewpro.co.kr/wp-content/uploads/2020/08/%EC%97%90%EC%96%B4%EC%BB%A8%EB%A9%94%EC%9D%B8.jpg',fit: BoxFit.cover),
                          ),
                        ),
                        SizedBox(height: 20,),
                        Text("냉장고"),
                        Text("7,000 원")

                      ],
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

복붙할수있는 코드블럭 입니다. 깃허브에서 프로젝트를 다운받으면 되지만, 아직 깃허브를 안배웠으니 복붙해서 쓰심됩니다.

 

 

ProductDetailPage.dart 파일의 내용입니다.

 

import 'package:flutter/material.dart';

class ProductDetailPage extends StatelessWidget {

  final String productName;
  final String imagePath;
  final String price;

  const ProductDetailPage({Key? key ,required this.productName ,required this.imagePath, required this.price}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("${productName} 상세페이지"),),
      body: Container(
        padding: EdgeInsets.symmetric(horizontal: 10),
        width: double.infinity,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(productName),
            SizedBox(height: 50,),
            Image.network(imagePath),
            SizedBox(height: 50,),
            Text(price)


          ],
        ),
      ),
    );
  }
}

코드 블럭입니다.


 

새 패키지를 만든뒤 숙제로했었던 코드를 모두 복사하여 붙여주세요.

import 의 경로같은경우 패키지 이름이 바뀌었으니 적절히 수정해주세요.

 

MainPage.dart

ProductDetailPage.dart

main.dart

pubspec.yaml 에 GetX 라이브러리 추가

이 네가지에 대해 조취해주시면 됩니다.

 

 

숙제를 하면서 답답한 부분이 있었을겁니다. 문제점은 대표적으로 3개인데요

 

1. 중복되는 위젯이 많다. 그리고 중복되는 문자열도 많다. 숙제의 경우 상품목록이 4가지였는데 이 4가지가 안에 내용만 다를뿐 같은형태를 띄고있죠. 그리고 상품명, 이미지경로, 가격이 각 상품위젯만들때마다 2번씩 사용되었습니다. (화면에 표시되기위해 적은것 과 ProductDetailPage의 파라메터로 적은것)

-> 컴포넌트위젯 만들기

 

2. Row로 2개씩 묶어줬는데 상품이 100개라면 Row를 50개를 만들어줘야하나. 그리고 100번씩이나 손아프게 작성해야하나?

-> 그리드뷰 , 반복문

 

3. 상품이 1000개라면? 그리고 상품정보가 수정될때마다 코딩을 새로해줘야하나?

-> 하드코딩 -> 데이터 와 로직기반 코딩

 

4. ProDuctDetailPage로 넘겨주는 파라메터가 지금은 3개라서 다행인데 10개라면? 너무 많이 적어줘야되서 해깔리진 않을까?

-> Vo클래스 만들기

 

 

 

이번 강의에서는 저 부분의 해결방법과 앱을 만들기위한 기타 기본적인 몇가지를 더 알아보겠습니다.

 

1. 컴포넌트

목차로돌아가기

 

MainPage.dart 의 상품들의 리스트를 보여주기위해 적었던 코드를 보면 상품부분이 중복되는것을 볼수있습니다.

잘안보인다면 코드를 - 를 눌러서 줄여서 확인해보세요.

우리가 숙제로 했던 프로젝트에서는 Expanded 부분이 상품개수만큼 중복되는것이 보일겁니다.

이 Expanded 중에 한개만 골라서 복사해주세요.

 

저는 lib -> component 디렉터리를 생성한후 다트파일 이름을 ProductWidget 이라고 지었습니다.

그리고 StatelessWidget 을 만든뒤 return 부분이 Scaffold 가 아니라 아까 복사해둔 Expanded 위젯을 넣었습니다.

 

ProductWIdget 의 멤버변수로 productName , imagePath , price 를 지정하고 내부로직에서 사용하도록 적절히 써넣었습니다.

 

MainPage.dart 로 와서 Expanded 부분을 지우고 ProductWidget 을 대신 사용해줍니다.

이렇게 Expanded 부분을 따로 컴포넌트 모듈화해서 사용을 하면 한 파일에대한 코드 길이도 줄어들고 여러가지 편한점들이 생깁니다.

나머지 Expanded 부분도 교체해주세요.

 

근데 문제점은 이렇게 해도 코드가 너무 길다는 문제가 있습니다. 조금 편하게 하기위해 상단에 변수를 몇가지 추가해줍시다.

배열타입 인데 그 내부 내용물들이 문자열로 구성되어있다면 타입을 List<String> 이라고 표현합니다. 그리고 리스트는 [] 이안에 내용물들을 넣고 내용사이에는 , 콤마로 표시해줍니다.

 

그래서 nameGroup[0] 이렇게하면 nameGroup 이라는 변수의 첫번째 정보를 가져오게됩니다.

0 이부분은 인덱스(index) 라는 부분인데 , 프로그래밍에서 index는 0부터 시작합니다. 첫번째가 인덱스 0 , 두번째가 인덱스1 이렇게 되니까 좀 해깔릴수도있지만 익숙해져야합니다. 

 

이렇게 하면 조금은 더 깔끔해진 모습을 볼수있습니다.

그런데 지금은 3가지의 변수만있지만 여러가지 변수가 있으면 더 복잡해질것입니다.

ProductWidget의 옵션넣기도 지칠것입니다.

그래서 Vo class 라는것을 만들것입니다.

2. Vo 클래스 만들기

목차로돌아가기

 

우리가 예전에 사람을 예로 들었을때

Person 클래스를 만들고 그안에 name 과 age 를 만든다음 Person(name : "철수", age:"22세") 이렇게 해봤던 적이 있습니다.

마찬가지로 Product 도 이름,이미지,가격 등의 정보를 가지고 있습니다. 그런 클래스를 Vo 클래스라고합니다.

 

나중에 네트워크 통신을 하게되면 정보를 받아서 Vo에 담아서 객체를 만드는데 이때는 Dto 라는 이름으로 만들게 됩니다

둘다 하나의 개념덩어리 클래스 라는건 같은데 Vo는 불변성 , Dto는 상황에 따라 멤버변수 변경 가능 이라는 점이 있는데 그런거는 그냥

개발팀의 의논에따라 정하면 됩니다. 그냥 본질은 같으니까 지금은 그냥 Vo라고 하고 넘어가도록 합니다.

 

vo 폴더를 만든다음 Product.dart 파일을 만들었습니다.

내부에 Product 라는 클래스 파일을 만들었습니다.

productName , imagePath , price 들은 String 타입이며 "?물음표" 를 붙여서 nullalbe(빈값가능) 하게 만들었습니다.

그리고 기본 생성자를 만들어 주었습니다.

 

(회사마다 , 또는 사람마다 코드짜는 방식이 다르지만 , 저는 Vo 를 만들때 변수들은 보통 null able 하게 만듭니다. 만약 null을 허용하지 않는다면 네트워크 통신할때 어떠한 이유로 price에 null 이 들어오게 되면 그 사소함 하나 때문에 에러가 발생해서 프로그램 자체에 영향을 줄수있기 때문입니다.

그래서 우리 앱 프로젝트 내에서는 Product 객체는 반드시 값들이 있어야 하지만 일단 변수들은 null able 하게 해두고 , 네트워크에서 null 값이 들어왔을때 "어떤문자열" 로 치환해주는 작업을 해주면됩니다. 이거는 http 통신에서 json 데이터를 다룰때 해볼것입니다.)

 

이 변수들을 클래스 내부에서 사용하게되면 이걸 멤버변수 라고 합니다.

그리고 printName 이라는 함수를 만들어 주었는데요, 클래스 내부에 있는 함수를 메소드(method) 라고 합니다.

 

MainPage.dart 파일로 와서 Product 인스턴스를 생성해줍니다.

상품이 4개라서 4개를 만들었습니다.

그리고 ProductWIdge에 item 옵션에 하나하나씩 넣어줍니다.

 

에러가 나니깐 ProductWIdget 을 수정하러 가줍니다.

 

멤버 변수와 생성자를 수정해줍니다.

 

그리고 item.productName 이런식으로 불러와서 값을 넣어줍니다.

여기서 item.productName??"" 이라고 쓴곳은 만약 item.productName 의 값이 null 이면 "" 을 사용해라 라는뜻입니다.

이렇게한 이유는 productName값을 우리가 넣어줬지만 이 값은 null 일수도 있기때문입니다.

우리가 Vo 를 만들때 ? 이걸 붙여서 nullalbe 하게 만들어줬기 때문이죠.

 

 

3.SingleChildScrollView

목차로돌아가기

 

만약 상품들이 1000개라면 Row위젯을 500개 만들어서 각 Row위젯마다 상품 2개씩을 넣어줘야 할 것입니다.

하지만 이건 너무 코딩같지않죠. 이럴때 GridView 라는걸 쓰게됩니다. 전체 몇개인지 ,어떤 위젯을 넣을것인지 , 1줄에 몇개씩 넣을지 알려주면 알아서 배열시켜줍니다. 그런데 이걸쓸때는 안에 위젯이 Expended 를 가지면안됩니다. 왜냐하면 위젯의 가로사이즈는 그리드뷰에서 관련하기때문입니다. 그래서 우리가 이번에 만든거는 Row 위젯을 사용하므로 어쩔수없이 Expended 사용했는데, 이걸 수정하지말고 그냥 새로 하나 다른예제 만들어서 GridView 를 사용해볼겁니다.

그런데 GridView 를 하기에 앞서 SingChildScrollView와 ListView 를 먼저 살펴보는게 순서인것 같아 SingleChildScrollView 를 먼저 알아보겠습니다.

 

ProductDetailPage.dart 파일에서 예제를 만들어 보겠습니다.

Text("구매자 목록") 으로 표시해두고 아래에 Column 위젯의 children 으로 일단 Container 위젯 하나만 적어보겠습니다.

그러면 말끔하게 잘 나옵니다.

 

그런데 Column 위젯의 children 으로 같은 Container 위젯 하나를 더주자 overflowed 에러가 나타나게 됩니다.

(에러가 안나면 Container 위젯을 한두개 더 넣어보세요.)

그이유는 데이터가 나타나는 부모의 세로 사이즈보다 부모안의 내용물의 길이 합이 더 길기때문입니다. 

 

이럴때는 Scroll 기능을 넣어주면 됩니다.

 

 

Scaffold 의 body 에 Container 위젯이 왔었는데 , 그 Container 위젯을 SingleChildScrollView 로 감싸줍니다.

 

그러면 스크롤 기능이 생기면서 Column 부분에 여러개의 Container 를 넣어도 사이즈 에러없이 잘 표시됩니다.

 

4. 4  ListView

목차로돌아가기

 

ListView 는 컬럼위젯 or 로우위젯 에 스크롤기능 + 알파 기능이 포함된 레이아웃 위젯입니다. 

ListView 를 공부하기 위한 페이지를 하나 만들어 보겠습니다.

 

MainPage 에서 ListView 위젯을 알아보기 위한 MemberListPage 로 가는 버튼을 하나 만들어주세요.

 

MemberListPage.dart 파일을 만들고 Scaffold 위젯의 body로 ListView 위젯을 줍니다.

그리고 하부에 임의 컨테이너를 복붙해서 화면에 넘칠만큼 넣어주세요.

그러면 스크롤 기능이 생기는것을 볼수있습니다.

 

ListView 의 특징은

1. children의 사이즈를 최대한 늘린다

여기서 분명 Container의 width 를 100으로 줬는데 그것 무시하고 가로로 최대한 넓혀 버리는걸 볼수있습니다..

근데 세로는 또 150으로 정확히 적용시키고 있습니다.

이건 ListView의 방향이 세로방향(Vertical) 일때 세로길이는 적용하고 가로길이는 ListView의 부모 width에 맞춰서 최대한 커지기때문입니다. 

그렇기 때문에 사이즈를 주고 싶을때는 ListView의 chidren 으로 오고있는 Container 를 다른 위젯으로 한번 감싸면 사이즈 조절이 가능합니다.

 

반면 scrollDirection을 Axis.horizontal(수평) 으로 했을때는 세로값이 무시됩니다.

 

2.ListView는 명확한 사이즈가 필요하다. ( Scroll기능이 들어가는건 다 명확한 사이즈가 필요 )

ListView 위젯 내부의 children 을 3개만 줘보세요.(모두 화면에 충분히 들어와서 Scroll이 발동안할만큼)

ListView 를 그냥 사용했을때는  아무 문제가 없습니다.

그런데 Column 으로 감쌋을때는 에러가 생깁니다.

그이유는 그냥 썼을때는 ListView의 사이즈가 Scaffold의 body가 가지고있는 사이즈를 그대로 받았기때문에 명확한 사이즈를 가지고 있다고 볼수있지만, Column 으로 감쌋을때는 ListView의 사이즈가 얼마인지 알수가 없습니다. 그래서 에러가 납니다.

이때 ListView를 Expended 로 감싸면 되긴하지만 , 지금은 그걸 이야기 하려고 하는게 아니라 shrinkWrap 이라는 옵션을 설명하기 위함입니다.

 

shrikWrap은 ListView의 사이즈를 chidren들의 합산으로 하는것이 되어 명확한 사이즈를 가지게 됩니다.

그래서 ListView를 다른 위젯과 함께 사용하고 , 리스트뷰의 사이즈를 chidren의 합산으로 딱 맞추고 싶다면 shirinkWrap:true 로 해주세요.

 

또는 ListView를 SizedBox 또는 Container 로 감싸서 사이즈를 명시적으로 줘서 명확하게 해주세요.

 

그런데 하고보니 , ListView를 꼭 써야하는가에 대한 의문이 듭니다.

Column 위젯을 사용하고 Column 위젯에 SingleChildScrollView를 쓰면 되는데 말이죠.

 

그래서 사실 자주쓰는건 ListView의 기능인 build 입니다.

 

저는 ListView의 크기를 Expanded 를 사용해서 , 멤버리스트와 , 구글광고 라고 적흰 위젯의 크기를 제외하고 나머지 공간을 ListView 가 같도록 해서 ListView의 크기를 명확하게 하였습니다.

 

ListView.build 를 사용해서 itemCount 옵션에 생성하고싶은 갯수를 넣습니다.

itemBuilder 옵션에 함수를 넣는데 위젯을 리턴하는 무명 함수를 넣으면 됩니다.

 

(context , index){

return SomThingWidget

}

 

** (context,index) 이 이름은 그냥 (c,i) 이렇게 아무거나 넣으면 되지만 그냥 다들 이렇게 쓰니까 불문률? 같은걸로 생각하시고 그냥 (context,index) 이렇게 적어주세요.

 

이런식으로 넣으면 됩니다. 이때 context부분은 신경쓰지마시고 index 부분을 봐주세요. 위젯을 itemCount만큼 itemBuilder에 있는 함수를 통해 생성하는데 그 함수가 생성되는 순서대로 index 값을 받을수있습니다. 그 index의 타입은 int 기 때문에 toString 을 써서 Text위젯에 넣어줘서 눈으로 볼수있게 하였습니다.

 

 

상단에 member 라는 List 변수를 만들어 주고 안에 멤버명을 적었습니다.

ListView 의 itemCount 옵션에 멤버가 3명이니까 3이라고 적어줘도 되지만 좀더 정확하게 member.length로 갯수를 적어주었습니다.

member[0] 이렇게 하면 List 타입의 member변수에서 첫번째값을 가져오라는 뜻인데 이걸 이용해서 Text위젯을 사용해 화면에 나타내주었습니다.

 

일반 ListView 와의 차이점은 ListView 는 하위 children 들을 한번에 화면에 모두 랜더링 하지만 builder는 스크롤되지않은부분은 랜더링 하지 않아서 , 수백개 이상 넘어가는 리스트가 있으면 builder 를 쓰면 좀더 메모리나 컴퓨팅자원측면에서 절약할수있습니다 

 

5.5 GridView

목차로돌아가기

 

GridView 위젯을 실습하기위해 페이지 하나 더 만들겠습니다.

 

Scaffold의 body에 GridView 위젯을 사용한뒤 children에 내용들을 적었습니다.

그리고 한줄에 몇개씩 배열할지 정해야하는데, 그걸 하기위해서는 gridDelegate 옵션에서 Slive~~~~~이런 길다란 위젯을 쓴뒤 그안에 옵션인 crossAxisCount 에다가 원하는 갯수를 적어줍니다.

 

그러면 1열에 2개씩 children 위젯들이 배치되는것을 볼수있습니다.

그런데 chidren의 Container 사이즈를 100 , 150으로 줬는데 그것들이 모두 무시되고 있습니다.

이것이 바로 GridView의 특징입니다. GridView는 가로사이즈를 최대한 가져가고, 그안에 chidren 위젯들을 한열의 갯수로 나누어서 최대한 가져가고,  사이즈는 기본값으로 가로:세로 비율 1:1로 나타나게 됩니다.

 

 

mainAxisSpacing 과 crossAxisSpacing 에 값을 적절히 줘서 간격을 조율할수있습니다.

GridView는 scroll 방향이 기본값으로 수직방향입니다. 

만약 GridView의 scroll 방향을 수평방향으로 하면 mainAxisSpacing 과 crossAxisSpacing 이 반대겠죠.

 

그리고 chilAspectRatio 로 가로세로 비율을 조정할수 있습니다. double 값을 넣으면 되는데 1.2, 1.3 이렇게 넣으면 됩니다. 그런데 직관적이지 않으니 5/4 이렇게 나누기가 들어간 수식을 줘도 됩니다.

Container의 width 와 height 는 의미가 없으니 지워도 됩니다.

 

 

GridView 에서 제공하는 count 기능을 쓰면 좀더 간편하게 할수있습니다.

 

ListView 와 마찬가지로 builder 기능을 써서 표현할수도 있습니다.

6.6 Stack

목차로돌아가기

 

Stack 위젯은 컴포넌트를 겹쳐주는 위젯입니다.

 

기존에 GridView.builder에서 itemBuilder 에서 retrun 되었던 Container위젯을 Stack 으로 감싸줍니다.

그리고 아래에 height 20, 빨강색의 컨테이너 박스를 추가했습니다.

그러니 에뮬레이터에서 보이는것처럼 겹쳐졌습니다.

그리고 Stack 위젯의 chidren 중에 가장 마지막에 적은것이 z축 기준 가장 위로 올라온다는걸 알수있습니다.

 

 

(GridView에서 height는 무시된다고 배웠는데 왜 height가 적용되는지 의아하실텐데 Gridview의 chidren으로 오는 위젯들의 크기가 조절된다는것이지 그 아래위젯들이 영향을 받는건 아닙니다.)

 

 Stack 위젯의 alignment 옵션을 조절하면 Stack이 쌓이는 정렬방식도 바꿀수있습니다.

 

임의로 위치를 조율하고싶으면 Positioned 위젯을 사용하면됩니다.

 

7.StateFulWidget

목차로돌아가기

 

1.상태변화값보기

StateFulWidget 을 실습해보기위해 페이지를 하나 생성해주세요. MyPage 라는 것을 만들었고 ,지금까지 페이지를 만들던 것과 마찬가지로 일단 StatelessWidget 으로 만들었습니다.

그리고 가운데 Container 박스를 만든다음 Text로 카운트를 표현하도록 했습니다.

GestureDetector 로 감싸주어서 클릭하면 count 가 콘솔에 나타나도록 했습니다.

 

 

  누르면 콘솔에 잘 나타납니다.

 

print(count) 아래에 count를 1식 증가시켜주는 함수를 더 적어주었습니다.

그렇다는건 일단 콘솔에 현재 count 를 표시하고 그다음 count 변수를 변화시킨다는 뜻입니다.

그리고 다음번에 한번더 클릭을 하게되변 변화된 count가 콘솔에 찍히게 됩니다. 그래서 콘솔에 1식 증가되는걸 볼수있습니다.

(1식 올릴때 count = count+1 대신에 count++ 를 쓸수있습니다.) 

 

그런데, 콘솔에는 count변수가 변하는걸 확실히 알수있는데 , 화면에서는 변화가 없습니다. 

hotReload를 한번 눌러보세요. 누르는 순간 현재 카운트로 변하게 됩니다.

 

이 이유는 stateless 위젯은 랜더링을 초기에 1번만 하기 때문입니다. 변수는 변할지언정 화면을 다시 그려주지 않아서 변화된 내용을 hotReload를 누르지 않는한 에뮬레이터에서 확인을 할수가 없습니다. 쉽게 말하면 UI 가 동적으로 변하지 않는다는것이죠. Flutter에서는 상태에 따른 변화가 없다고 해서 Stateless라고합니다. (여기서 상태라는건 위젯을 구성하는 데이터 정도로 이해하면 될 것 같습니다.)

 

반면에 상태의 변화에 따른 어떤 액션이 있는것을 StateFul 하다고 하고 그런 기능을 가지고 있는 위젯이 StateFulWidget 이라고 합니다. 

 

StatelessWidget 을 StatefulWidget 으로 바꿔주겠습니다.

 

전부지우고 StatefulWidget 을 만들어줘도 되지만, StatelessWidget에 커서를 대고 Alt+enter 하면 손쉽게 바꿀수있도록 안드로이드 스튜디오에서 지원을합니다.

 

바꿨더니 class 가 2개가되었습니다.

dart 파일 1개당 1개의 class 가 있어야 하는데 , Stateful 은 저렇게 한세트라고 생각하시면됩니다.

 

StateFulWidget 에서는 setState  라는 함수를 사용할 수 있습니다. 이 함수는 화면을 재랜더링 하는 함수입니다.

함수 아래부분에 setState 라는 함수를 사용 해 보세요. 

그러면 화면의 카운터도 올라가는것이 보일것입니다.

 

 

satState((){

 

여기

 

})

 

구현하고자하는 내용을 setState 내부에 넣어보세요. 이뜻은 이내부에 함수가 실행된뒤에 setState 가 실행된다는 뜻입니다.

 

2. 부모의 멤버값 사용하기

name 이라는 멤버변수를 받도록 했습니다.

(final 이나 const 라는게 있을때도 있고 없을때도 있고 해서 해깔릴수있는데 그냥 일단 작성해줍니다. 나중에 언제써야할지 자연스럽게 알게될 것입니다.)

 

appbar 에 넘어온 name 을 쓰려고하는데 잘안될 것입니다. 이때 widget. 을 붙여서 변수를 사용해보세요.

아래 _MyPageState 클래스 안에 변수는 그냥 사용 할 수있습니다.

MyPage(부모) 클래스와 _MyPageState(자식) 는 부모 자식관계라고 보면되는데 자식이 부모꺼 쓸때는 widget. 이렇게 쓰면됩니다.

 

3.initState

 

Stateful 위젯은 initState 라는 함수를 사용할수있습니다.

_MyPageState(자식) 클래스에 ini만 쳐도 자동완성으로 작성할수있습니다.

 

이함수는 해당 위젯이 실행될때(화면에 첫 랜더링 될때) 가장 맨처음 한번만 실행되는 함수입니다.

그래서 변수의 초기값 설정에서 주로 사용을 합니다.

 

 

4.LifeCycle 생명주기

 

 

StatelessWidget 은 라이프라이클 이라고 할만 것이 없습니다.

그냥 처음생성되면 생성자 함수가 실행되고 내용물 빌드하게 됩니다..

 

반면에 StatefulWidget 은 조금 공부할것이 있습니다.

 

생성자 생성 이후 위에 우리가 만든 것처럼 MyPage 에서 _MyPageState 라는 클래스를 만듭니다.

그다음 initState 가 실행되고 didChageDependencies 가 실행됩니다.

initState 는 위젯 이 생겨서 사라질때까지 딱 1번만 실행이되고, didChangeDependencies 는 처음에 한번 실행된 이후

의존성정보에 변화가 생기면 실행되서 Build() 를 새로 합니다.

 

setState 는 사용자가 Build 재호출을 요청하는 것입니다. 현재 데이터 기반으로 Build 를 새로 해줍니다.

 

didUpdateWidget 은 부모위젯. 즉 MyPage class 에 변화가 일어났을때 호출되는 함수입니다. 부모위젯이 변경되면 생성자부터 타고내려와서 Build 함수가 실행됩니다.

 

그리고 마지막으로 위젯이 사라질때 dispose 함수가 실행됩니다.

 

일단 지금 단계에서는 initState 와 setState 만 알고 넘어가도록 합시다.

 

8. 조건문

목차로돌아가기

 

dart 언어 공부를 좀 해보도록 하겠습니다.

조건문에 대해 다룰텐데,  조건문은 모든 프로그래밍 언어에 모두 있는 구문입니다.

그러니 다른 언어의 경험이 있으시면 패스하셔도 되는 부분입니다.

 

flutter 에서 조건문은

 - 메소드 내부에서의 조건문

 - 1. -> if 문

2 ->. switch 문

 

- build 메소드 내부의 랜더링되는 위젯의 조건문으로 나뉩니다.

 - 3 -> 특정조건에만 나타나는 위젯

 - 4 -> 3항연산자 조건문

 

checkCount 라는 void 타입의 메소드를 만들었습니다.

 

그안에 if 문을 사용했는데요

 

if( 여기가 참이면 ) {

참이면 여기 내용이 발동

}. else{

거짓이면 여기 내용이 발동

}

 

이런 형태로 되어있습니다. 

 

그리고  저기 파란색 Container 를 누르면 count 가 올라가고 checkCount 메소드를 호출하도록 했습니다.

count 가 5보다 작으면 over5 라는 bool 타입의 변수는 false 로 , 그렇지 않다면 true 로 값을 가지도록 해두었습니다. 

제일 아래 노란색 박스는 if(참이면) 바로 아래에 있는 위젯이 나오게하라 라는 뜻입니다.

 

버튼을 눌러 count 가 5가되게 했더니 빨간색으로 표시한 부분이 나타나게 됩니다.

 

그럼 벌써  13 부분을 했네요.

 

1 부분을 좀더 살펴보면 예시에서는 2가지 조건만 했습니다. 5이상 인지 , 아닌지 만요. 근대 조건이 여러개면 어떻게 될까요?

 

이런식으로 else if 를 통해 3개든 4개든 확장할 수 있습니다.

이렇게 if 문은 위에서부터 아래로 읽다가 조건이 아닌곳에서 멈추고 if 문을 빠져나오게 됩니다.

 

참고로 && 는 and 연산자 라고 하며, || 는 or 연산자 라고합니다

A && B 라고 했을때 A와 B가 모두 참이어야 참이됩니다.

A || B 라고 했을때 A와 B 둘중에 하나만 참이어도 참입니다.

 

 

Text 위젯의 글자색상을 바꿔보았는데 count가 10이상이면 파랑색 , 그렇지 않다면 빨강색으로 나오게 했습니다.

 

count >=10 ? Colors.blue : Colors.red

 

이렇게 되어있는데요, 이것을 삼항연산자라고 합니다. ?물음표 와 :콜론을 사용합니다.  이렇게 4 번 부분도 보았습니다.

 

 

 

switch 문을 알아보기 위해 checkName 이라는 void 타입의 함수를 만들었습니다.

 

비교할 변수를 switch ( 괄호 ) 괄호부분에 두고 case "조건넣기" 이런식으로 사용하면 됩니다. 

switch 문을 읽어가면서 조건에 걸리는게 있으면 :(콜론) 뒤에 행위를 하고 break 을 통해 switch 문을 벗어나게 되는것입니다. default 는 case 에 걸리는게 아무것도 없을때 어떻게 해줄것이냐 를 적어넣으면 됩니다.

이렇게 3 부분도 보아서 1,2,3,4 조건문을 모두 알아보았습니다.

 

 

9.  반복문

목차로돌아가기

 

반복문이란 말그대로 어떤행위를 반복하도록 하는 문장입니다.

반복문에는 for 문과 while 문이 있습니다.

for문은 지정된 횟수만큼 반복하는 반복문이고

while 문은 조건이 참인동안 반복하는 반복문입니다. 조건이 거짓이 되는순간 멈추게되죠.

 

while 문 같은 경우는 제가 거의 사용을 하지않고, 지금 단계에서는 구지 while 문까지 알아갈필요도 없는것 같아서 for 문에 대해서만 다루려고 합니다.

 

flutter 에서 사용하는 for 형태의 반복문의 크게 3가지 입니다.

1. 일반 for 문

2. for in 문

3. forEach (3번은 좀 특별한게 일반적인 for 문이 아니고, Iterable 컬렉션(List, Set, Map) 에서 제공하는 for 처럼 사용할수있는 기능 입니다.)

 

1. 일반적인 조건문은 이렇게 되어있습니다. 초기화 , 조건식 , 증감식을 적어줘야합니다. 

 

초기화 -> 조건 확인 -> 함수실행 -> 증감식 실행 -> 조건 확인 -> 함수실행 -> 증감식 실행 -> 조건 확인 ->...

이런식으로 진행되다가 조건이 안맞으면 for 문이 종료됩니다.

 

일정 조건일때 for 문을 건너뛰기 할수도있습니다.

age 가 i 와 같을때는 넘어가란 의미로 continue 를 써줬습니다. continue 를쓰면 for문이 종료되는것이 아니고 그냥 아무것도 안하고 다음으로 넘어가게 됩니다.

물론 해당 예시에서는 if 와 else 를 이용해서 처리할수도 있을것입니다.

그런데 continue 라는걸 알려드리기 위해 작성해보았습니다.

 

for 문을 강제 종료 할때는 break 을 사용하면 됩니다.

for 문이 포함된 함수를 완전 종료하고 싶다면 return 을 사용하면됩니다.

 

2. for in 문은 컬렉션(list,set,map) 변수를 하나 생성하고 그 list 의 갯수만큼 실행하는 것입니다.

 

 

이런식으로 사용하면 됩니다. 반복갯수가 타겟변수의 갯수에 딱맞춰져서 실행됩니다.

변수의 내용물을 하나하나 순회하면서 실행하는 방식으로 index 를 구할수가 없다는 점이 특징입니다.

 

 

forEach 방법은 컬렉션 타입의 List ,Set ,Map 에서 제공하는 기능입니다.

element 부분은 friends 변수의 하나하나의 요소입니다.

그런데 해깔리고 직관적이지 않다싶으면 나중에 익숙해 지기전까지 그냥 일반 for문을 사용하셔도 아무상관이 없습니다.

 

우선 flutter 첫바퀴 에서는  for 문에 대해 이정도만 알고 가면 될 것 같습니다.

 

 

10. 변수타입

목차로돌아가기

 

우리는 지금 변수를 사용하면서 앞에 변수타입이라고 하여 String ,int 뭐 이런거를 적어줬습니다. 이게 변수 타입이라는 것인데, 타입에 대한 자세하고 근본적인거는 경험상 일단 flutter를 한바퀴 다돌고나서 생각해도 괜찮다고 개인적으로 생각합니다. int는 메모리에 어떻게 저장되고 String이나 객체타입은 메모리에 어떻게 저장되고 이런부분도 알아야하지만, 일단 지금은 어떤타입이 있구나 정도만 설명합니다.

이부분도 이미 프로그래밍을 기존에 접했던분은 패스해도 되는 부분입니다.

 

  1. int: 정수 값을 저장하는 변수 타입입니다. 예를 들어, int age = 30;와 같이 사용할 수 있습니다.
  2. double: 부동 소수점 값을 저장하는 변수 타입입니다. 예를 들어, double price = 29.99;와 같이 사용할 수 있습니다.
  3. String: 문자열 값을 저장하는 변수 타입입니다. 예를 들어, String name = "John";와 같이 사용할 수 있습니다.
  4. bool: 불리언 값을 저장하는 변수 타입으로, true 또는 false 값을 가집니다. 예를 들어, bool isAlive = true;와 같이 사용할 수 있습니다.
  5. dynamic: 동적 타입으로, 어떤 타입의 값을도 저장할 수 있는 변수 타입입니다. 변수에 어떤 값을 할당하느냐에 따라 자동으로 타입이 결정됩니다. 예를 들어, dynamic value = 42; 또는 dynamic value = "Hello";와 같이 사용할 수 있습니다.
  6. List: 배열 혹은 리스트를 나타내는 변수 타입입니다. 예를 들어, List<int> numbers = [1, 2, 3, 4];와 같이 사용하면 정수 배열을 저장할 수 있습니다.
  7. Map: 맵 또는 딕셔너리를 나타내는 변수 타입입니다. 예를 들어, Map<String, int> scores = {"John": 90, "Jane": 85};와 같이 사용하면 문자열을 키로 갖는 정수 값을 가지는 딕셔너리를 저장할 수 있습니다.
  8. Object: 모든 객체를 나타내는 최상위 타입입니다. 모든 클래스의 인스턴스는 Object 타입으로 할당될 수 있습니다. 

이미 int,double,String,bool,List,Object(Person예시로 들 때) 는 강의중에 한번씩 봤던것들이죠?

 

(그런데 강의중 void 타입의 함수 이렇게 설명한적이 있는데 , void 는 함수의 리턴 타입중 하나로 아무것도 리턴하지 않는 타입입니다. 말이좀 이상하죠

 

예를들어

 

1. String getName(

return "철수"

이런 함수가 있다고 했을때

이렇게 하면  String name = getName() 하면 name = "철수" 가 됩니다.

 

2. void sendEmail(){

print("이메일 보냄")

}

이렇게 하면 sendEmail() 하면 콘솔에 뭔가 나타날뿐 아무런 결과값을 받지 않게 됩니다.

구지 리턴값을 받을필요가 없을때 void 타입의 함수를 사용합니다.)

 

Homework

목차로돌아가기

 

이번숙제도 다음편과 연관되니 꼭 해주세요.

 

 

1. 그리드뷰와 리스트뷰 모드를 전환할수 있는 버튼을 만드세요. 그래서 각 모드에 맞게 뷰가 나오게 해주세요.

2. 리스트뷰의 이미지의 모서리를 둥글게 만들어보세요. =>ClipRRect 위젯

3. 각 아이템을 눌렀을때 상세페이지로 이동해보세요

4. 상세페이지에서 하트를 누르면 꽉찬 하트가 나오게 해보세요. 그리고 다시누르면 빈하트가 나오게해주세요. =>Icon위젯

'코딩생초보를 위한 플러터 빠르게 한바퀴' 카테고리의 다른 글

6.network  (0) 2023.08.22
5.state management  (0) 2023.08.11
4.Json  (0) 2023.08.09
2.first my project  (1) 2023.07.29
1. create project  (0) 2023.07.29