🧑‍💻 Flutter 공부

숙제) 당근마켓 UI 따라해보기 - 진짜 머리털 다 빠지겠다

wham here 2023. 8. 26. 17:30

저번 시간(4강)에 코딩 애플에서 당근마켓에 있는 이 리스트용 컴포넌트를 최대한 비슷하게 만들어오라구 했다.

5강이 자동으로 넘어가서 끄려고 했는데 마침 제목이 '숙제 안해오면 때림'이라서 더 급하게 꺼줌,, , ㅎ

오늘 아침 11시부터 했는데 생전 처음 개발해보는 복잡한 구성에 계속 되는 에러에 머리털 다 빠지는 줄.,,,

이래서 개발자 대머리 밈이 만은 걸가..,,,.?,??,.,?

아무튼 오늘은 내가 했던 순서대로 대충 차례 정해봄


1. 계속되는 에러의 향연..

2. ♡4 넣기

3. Container 높이 지정해서 저쪼아래 박스를 데려오자,,

4. Row 안에 있는 Container에 마진을 주자

5. Column의 children으로 추가한 텍스트들 왼쪽 정렬

6. 타이틀 maxLine 지정

7. 자잘한 텍스트 디자인들

8. 이미지 넣고 Radius 넣기

완 성 . . . . . !


1. 계속되는 에러의 향연..

아니 에러 때문에 미리보기도 전혀 안되네 엉엉

대체 뭐가 문젠데ㅠㅠㅠㅠㅠ

이 상태로 이거저거 만져보다가 1~2시간이 지나버렸다...

환장 :)

오.

]를 엉뚱한 곳에 쓴걸 깨달아서 Row 바로 안으로 내려보내줬더니 에러는 없어짐… 후,,,

 

근데 왜 박스가 저 밑에 가있냐…?

아 Container 높이 지정 안해줘서 그런가?

잘 보니 텍스트가 그냥 가운데 정렬이 아니라 왼쪽에 딱 박스 크기 만큼 비우고 시작되는 걸 보니 그런 것 같다.

나중에 이미지 자세히 보니까 내가 Container 컬러를 white로 지정해둬서 살짝 진한 배경색과 구분이 되어 보이긴 한다.

이거 나중에 이미지 박스 크기 조정할 때 눈치챔 엉엉…

 

 

 

 

2. ♡4 넣기

♡ 아이콘과 숫자 4를 Row로 엮은 부분이 Column의 가장 마지막에 와야 했는데,

이거라도 먼저 다 넣어두고 정렬해보자…

근데 다 까먹어서 다시 옛날꺼 계속 왔다갔다 하면서 보면서 했닼ㅋㅋㅋ ㅠㅠ

Row(
    children: [
	    Icon(Icons.favorite_border),
	    Text('4'),
    ],
)

처음에는 이렇게만 작성했는데, Material에서 기본으로 제공하는 크기와 색상으로 대충 들어가있어서 전혀 비슷하지 않았다.

  • 아이콘과 텍스트 컬러를 grey로 바꿔줘야 했고
  • 아이콘과 텍스트 사이즈를 각각 바꿔서 서로 비슷한 크기로 만들어야 했고
  • Container의 남은 폭 내에서 자동으로 가운데 정렬이 된 텍스트들을 왼쪽으로 맞춰준 후
  • ♡4 부분만 우측으로 정렬해야 했다!

일단 컬러와 사이즈부터.

아이콘은 사이즈를 변경하는 법을 따로 배우지 않았어서 그냥 구글링 했다.

별거 없고 그냥 size: 파라미터를 사용하면 됨!

Row(
    mainAxisAlignment: MainAxisAlignment.end,
    children: [
	    Icon(
		    Icons.favorite_border,
		    color: Colors.grey,
		    size: 16
		    ),
	    Text(
		    '4',
		    style: TextStyle(
		    color: Colors.grey,
		    fontSize: 14
		    ),
	    ),
    ],
)

 

 

 

 

3. Container 높이 지정해서 저쪼아래 박스를 데려오자,,

Container width, height를 입력하고 정렬 해줬더니 에러 뜸…

픽셀이 오버됐다는데???

엄…. 뭐가 넘어간거지? 너비가 넘어갔다구 하네…

아 Container 너비를 100으로 임시로 준게 좁아서 글쿠낭!

 

Container의 width를 저번에 배운 double.infinity로 줘서 해결!!

자세히 보면 Container의 색상을 white로 지정해둬서 그런지 살짝 차이가 보인다.

가로 폭은 확실히 좌우 꽉 채워지게끔 잘 적용됨!

 

 

 

 

 

4. Row 안에 있는 Container에 마진을 주자

박스가 너무 작고 마진이 없으니 벽에 딱 붙어있어서 안정적으로 배치해줘야 한다.

Container(
          width: 128,
          height: 128,
          margin: EdgeInsets.all(16),
		)

요렇게 사방에 마진 16씩 주고,

가로 세로 폭은 욕심 같아선 부모 높이에 맞게 자동으로 정사각형이 되면 좋겠는데 어려워서 일단 포기…

부모 Container 높이를 160으로 해두었기에, 상하 16씩 32를 뺀 128을 입력해줬다.

 

.

 

 

 

 

5. Column의 children으로 추가한 텍스트들 왼쪽 정렬

이제 텍스트 왼쪽 정렬 해주자…

이건 또 어떻게 하느냐… 검색!

https://velog.io/@jong/Flutter-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%A0%95%EB%A0%AC-1

 

[Flutter] 텍스트 정렬 1

사진과 같이 텍스트 정렬

velog.io

찾아보니 생각보다는 내가 해줄 게 많지 않았다.

그냥 Column 바로 밑에 크로스축 정렬 얘기해주면 되는 거였음…

child: Column(
	     crossAxisAlignment: CrossAxisAlignment.start,
         children: [
			]
		)

편의상 밑에 있는 children 내부는 생략 ㅎ…

진짜 당근마켓 UI랑 비슷해지고 있다구… 엉엉

이제 기본으로 들어간 텍스트 사이즈나 두께 조정하면서 더 비슷하게 디자인해주면, 오른쪽은 끝남!

 

 

 

 

 

6. 타이틀 maxLine 지정

제품명 텍스트를 w700으로 바꾸고, 사이즈를 20으로 키웠더니 또 픽셀이 넘어갔다는 에러가 보인다.

줄바꿈이 안되어있구나. 구글링 고고 ,,

https://d-dual.tistory.com/7   요 글을 보고 따라해봤다.

 

[ Flutter ] 길이에 따라 Text 자동 줄바꿈 / Flexible Text

Flutter에서 작업중 Text를 내가 정한 Width 길이 및 줄을 설정하여 그 이후의 텍스트들은 보이지 않게 하는 방법.🥳 나는 상세 페이지 이전에 미리보기 페이지에서 텍스트가 너무 길어질 경우를 대

d-dual.tistory.com

 

Container(
	child: Column(
		crossAxisAlignment: CrossAxisAlignment.start,
		children: [
			Flexible(
				child: RichText(
					overflow: TextOverflow.ellipsis,
					maxLines: 2,
					strutStyle: StrutStyle(fontSize: 14.0),
					text: TextSpan(
						text: '캐논 DSLR 100D (단렌즈, 충전기 16기가SD 포함)',
						style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18)
            ),
          )
	      ),
      Text(
	      '성동구 행당동 · 끌올 10분 전',
        style: TextStyle(
	        color: Colors.grey,
          fontSize: 12
          ),
        ),
      Text(
	      '210,000원',
        style: TextStyle(
        fontWeight: FontWeight.w700,
        fontSize: 16
        ),
      ),
      Row(
	      children: [
	        Icon(
	          Icons.favorite_border,
	          color: Colors.grey,
	          size: 16
          ),
         Text(
	         '4',
           style: TextStyle(
	           color: Colors.grey,
	           fontSize: 14
	           ),
         ),
      ],
    )
	]
),
),

다음 강의에서 Flexible을 가르쳐주는 것 같던데, 숙제하면서 일부러 해오라고 한건가보다!

예시 코드에 Flexible 위젯이 쓰여있고, RichText라는 위젯도 있다.

최대 2줄까지만 보여주도록 2로 숫자를 바꿔서 적어줬다.

strutStyle은 뭔지 아직 모르겠다.

TextSpan은 저번에 E님이 티스토리 블로그 폰트 바꿀 때 이렇게 쓰라고 가르쳐줬던 단어인데 옹… 여기저기 쓰이는 애구나 싶었다.

 

 

 

 

 

7. 자잘한 텍스트 디자인들

  • 근데 이렇게 하니까 반응형처럼 텍스트가 1줄이 됐다가 2줄이 됐다가 유연하게 되질 않아서 일단은 width를 300 정도로 줬다.
  • 그리고 왼쪽 이미지 넣을 박스는 박스 자체에 마진을 줘서 괜찮은데, 오른쪽 위/오른쪽에 글자가 딱 붙어버리길래 다시 왼쪽을 제외하고 16씩 마진을 줬다.
  • 근데 엄… 간격이 너무 또 없네… 이거 다 padding으로 넣는 방법밖에 없는 걸까…
    • Figma에서는 항상 Auto Layout 기능 안에서 간격 수치를 정할 수 있는데ㅠ
    • 구글링해보니까 Padding 말고도 height만 지정한 SizedBox를 사이사이에 넣으면 된다고 한당! 🤓
  • 아까 못했던 ♡4 부분을 우측으로 보내주는 것도 해야 한다.
    • 이것도 따로 검색해서 해당 Row 바로 밑에 mainAxisAlignment: MainAxisAlignment.end, 을 넣어주면 된다는 걸 알게됨!!!

👇 그래서 이렇게 되었다. 👇

Container(
	**width: 300,
  margin: EdgeInsets.fromLTRB(0, 16, 16, 16),**
	child: Column(
		crossAxisAlignment: CrossAxisAlignment.start,
		children: [
			Flexible(
				child: RichText(
					overflow: TextOverflow.ellipsis,
					maxLines: 2,
					strutStyle: StrutStyle(fontSize: 14.0),
					text: TextSpan(
						text: '캐논 DSLR 100D (단렌즈, 충전기 16기가SD 포함)',
						style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18)
            ),
          )
	      ),
			**SizedBox(height: 8,),**
      Text(
	      '성동구 행당동 · 끌올 10분 전',
        style: TextStyle(
	        color: Colors.grey,
          fontSize: 12
          ),
        ),
			**SizedBox(height: 8,),**
      Text(
	      '210,000원',
        style: TextStyle(
        fontWeight: FontWeight.w700,
        fontSize: 16
        ),
      ),
      Row(
				**mainAxisAlignment: MainAxisAlignment.end,**
	      children: [
	        Icon(
	          Icons.favorite_border,
	          color: Colors.grey,
	          size: 16
          ),
         Text(
	         '4',
           style: TextStyle(
	           color: Colors.grey,
	           fontSize: 14
	           ),
         ),
      ],
    )
	]
),
),

 

 

 

8. 이미지 넣고 Radius 넣기

초반에 이미지 넣어서 해보려다가 자꾸 뭔가 에러 나서 미뤄놨던거 다시 함,…

이미지는 더 등록하지 않고 그냥 있던거 그대로 가져옴!

https://cherry-forest.tistory.com/20

 

[Flutter] 이미지 크기 조절 fit 옵션 (BoxFit)

이미지 크기를 설정할 때 width, height 값을 주면 되고, BoxFit 클래스를 이용하면 설정한 크기 안에서 이미지 크기를 조절할 수 있다. Image.asset( 'test.png', width: 500, height: 250, fit: BoxFit.cover ), Boxfit.fill,

cherry-forest.tistory.com

여기에 이미지 핏에 대한 설명이 자세히 나와있어서 참고했다.

내가 원하는 건 특정 사이즈에 이미지 비율이 깨지지 않도록 최대한 채우는 거라서,

5. Boxfit.fitHeight를 활용해봤는데 이미지가 잘 들어가긴 하는데

내가 원하는 radius 효과가 안들어가길래 그냥 다른 방법을 더 찾아봤다.

 

 

https://cpcp127-app-dev.tistory.com/9

 

Flutter - Container안에 이미지 넣기

https://cpcp127-app-dev.tistory.com/8 Flutter - Container container 말 그대로 박스 같은 개념으로 보면되고 colum이나 row처럼 여러개의 자식은 가지는 children[]은 사용안하고 한개의 자식만 가져서 child를 사용합

cpcp127-app-dev.tistory.com

응… radius 하려면 그냥 박스 그리기는 해야 하나보다 싶어서 다시 이런걸로 찾아봄…ㅋㅋ큐ㅠㅠ

아까 지웠던 Container 다시 살렸다.

Container의 decoration 내에 이미지를 넣는 방식이었다.

DecorationImage()라는 위젯도 있었군,,,

이렇게 해서 드디어 완성 ㅠㅠ

 

 

 

 


완 성 . . . . . !

자랑스러우니까 이것도 한번씩 더 올려….

내가 몇번이나 머리를 쥐어뜯었던가... 에구구...

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          actions: [
            Icon(Icons.search, color: Colors.black87,), Icon(Icons.menu, color: Colors.black87,), Icon(Icons.notifications_none_outlined, color: Colors.black87,)
          ],
          title: Text(
            '금호동3가', style: TextStyle(color: Colors.black87, fontWeight: FontWeight.w700),
          ),
          backgroundColor: Colors.white,
        ),

        body: Container(
          width: double.infinity, height: 160, color: Colors.white,
          child: Row(
            children: [
              Container(
                width: 128,
                height: 128,
                margin: EdgeInsets.all(16),
                decoration: BoxDecoration(
                  image: DecorationImage(
                    image: AssetImage('assets/fan2.png'),
                  ),
                  borderRadius: BorderRadius.circular(8),
                ),
              ),

              Container(
                width: 300,
                margin: EdgeInsets.fromLTRB(0, 16, 16, 16),
                child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Flexible(
                        child: RichText(
                            overflow: TextOverflow.ellipsis,
                            maxLines: 2,
                            strutStyle: StrutStyle(fontSize: 14.0),
                            text: TextSpan(
                              text: '캐논 DSLR 100D (단렌즈, 충전기 16기가SD 포함)',
                              style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18)
                                ),
                            )
                      ),
                            SizedBox(height: 8,),
                            Text(
                              '성동구 행당동 · 끌올 10분 전',
                              style: TextStyle(
                                  color: Colors.grey,
                                  fontSize: 12
                              ),
                            ),
                            SizedBox(height: 8,),
                            Text(
                                '210,000원',
                              style: TextStyle(
                                fontWeight: FontWeight.w700,
                                fontSize: 16
                              ),
                            ),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.end,
                              children: [
                                Icon(
                                Icons.favorite_border,
                                color: Colors.grey,
                                size: 16
                                ),
                                Text(
                                '4',
                                style: TextStyle(
                                color: Colors.grey,
                                fontSize: 14
                                ),
                              ),
                            ],
                          )
                  ]
                ),
              ),
            ]
          ),
        ),
      ),
    );
  }
}
반응형