Flutter Basic Chapter 2 - 다크 테마 월렛 대시보드

헤더와 다크 배경 구성

🎯 이번 단계에서 배울 것

  • 화면 전체 배경색과 여백을 잡아 기본 레이아웃 만들기
  • Row, Column을 조합해 우측 상단 인사 헤더 정렬하기
  • 불투명도(Opacity)를 이용해 텍스트 명암 대비 조절하기
  • 다크 테마 UI에서 자주 사용하는 색상 코드를 적용하기

📂 파일 구조

1
2
수정되는 파일
- lib/main.dart (헤더 UI 추가)

📝 1단계: 헤더 UI 배치하기

전체 코드 (lib/main.dart @ 2.0):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import 'package:flutter/material.dart';

void main() {
runApp(App());
}

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Color(0xFF181818),
body: Padding(
padding: EdgeInsets.symmetric(
horizontal: 40,
),
child: Column(
children: [
SizedBox(
height: 80,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'Hey, Selena',
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
Text(
'Welcome back',
style: TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.8),
fontSize: 18,
),
),
],
)
],
)
],
),
),
),
);
}
}

🔍 코드 상세 설명

1. 다크 테마 배경 지정

1
backgroundColor: Color(0xFF181818)
  • 16진 색상 코드로 거의 검정에 가까운 회색을 지정했습니다.
  • 다크 테마에서는 고대비 흰색 텍스트와 조합해 가독성을 확보합니다.

2. 전체 여백과 상단 패딩 확보

1
2
3
4
Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
)
SizedBox(height: 80)
  • 좌우 40px 여백으로 콘텐츠가 화면 모서리에 붙지 않게 합니다.
  • SizedBox로 상단 공간을 만들어 노치 영역이나 상태바와 겹치지 않도록 합니다.

3. 헤더 정렬 전략

1
2
3
4
5
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
  • Row를 사용해 헤더 전체를 오른쪽 끝에 배치했습니다.
  • 내부 ColumncrossAxisAlignment.end로 맞춰 두 줄의 텍스트가 오른쪽 정렬되도록 했습니다.

4. 텍스트 스타일 대비

1
color: Color.fromRGBO(255, 255, 255, 0.8)
  • 두 번째 문자열은 살짝 투명도를 주어 강조 계층을 만들었습니다.
  • fromRGBO의 마지막 파라미터(0.8)가 알파값(불투명도)입니다.

시각화:

1
2
3
4
5
6
┌──────────────────────────────────┐
│ │
│ Hey, Selena│
│ Welcome back│
│ │
└──────────────────────────────────┘

🎨 화면 미리보기

1
다크 배경 + 우측 상단 인사말 두 줄

📊 동작 흐름

1
2
3
4
5
1. Scaffold가 0xFF181818 배경을 그림
2. Padding이 좌우 여백을 확보
3. Column이 세로 레이아웃을 구성
4. Row가 헤더를 오른쪽 끝으로 정렬
5. Column 안에서 두 줄 텍스트가 위→아래 순으로 출력

✅ 체크리스트

  • 배경색과 텍스트 색 대비가 충분한가?
  • 헤더가 화면 오른쪽 상단에 맞춰졌는가?
  • 두 번째 텍스트가 살짝 흐리게 표현되는가?

💡 연습 과제

  1. 헤더 이름을 본인 이름으로 바꿔보세요.
  2. SizedBox(height: 80) 값을 줄여 상단 여백 변화를 확인하세요.
  3. Color.fromRGBO의 알파값을 0.5로 변경해 투명도 차이를 느껴보세요.

잔액 정보와 CTA 버튼 추가 (feat. Const 최적화)

🎯 이번 단계에서 배울 것

  • Column에 텍스트를 차곡차곡 쌓아 계층적 정보를 표현하기
  • withOpacity로 텍스트 색상 밝기를 빠르게 조절하기
  • ContainerBoxDecoration으로 버튼 형태 만들기
  • const 키워드로 위젯을 고정해 성능과 핫리로드 효율 개선하기

📂 파일 구조

1
2
수정되는 파일
- lib/main.dart (잔액, 버튼 레이아웃 추가 및 const 적용)

📝 1단계: 잔액 섹션과 버튼 만들기

추가된 코드 핵심 (lib/main.dart @ 2.2~2.3):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const SizedBox(height: 120),
Text(
'Total Balance',
style: TextStyle(
fontSize: 22,
color: Colors.white.withOpacity(0.8),
),
),
const SizedBox(height: 5),
const Text(
'\$5 194 482',
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
const SizedBox(height: 30),
Row(
children: [
Container(
decoration: BoxDecoration(
color: const Color(0xFFF1B33B),
borderRadius: BorderRadius.circular(45),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 20,
horizontal: 50,
),
child: Text(
'Transfer',
style: TextStyle(fontSize: 20),
),
),
)
],
),

🔍 코드 상세 설명

1. withOpacity로 명암 조절

1
color: Colors.white.withOpacity(0.8)
  • 이미 존재하는 컬러에 간단히 투명도를 입혀 강조 수준을 조절합니다.
  • RGBO보다 간결하여 빠르게 반복 적용할 수 있습니다.

2. 큰 숫자 폰트와 두께

1
2
fontSize: 48,
fontWeight: FontWeight.w600,
  • 잔액 정보는 한눈에 들어와야 하므로 큰 글씨와 굵은 두께를 사용합니다.
  • Flutter의 FontWeight는 100~900 사이 굵기를 선택할 수 있습니다.

3. 버튼 레이아웃

1
Container → BoxDecoration → borderRadius: BorderRadius.circular(45)
  • 컨테이너 배경색을 노란색으로 칠하고 모서리를 둥글게 잘랐습니다.
  • Padding으로 내부 여백을 주어 버튼이 답답해 보이지 않게 합니다.

4. const 키워드 활용 (2.3 커밋)

  • 값이 변하지 않는 위젯 앞에 const를 붙여 위젯 트리를 불변으로 표시합니다.
  • Flutter는 const 위젯을 재사용하므로 리빌드 때 성능이 좋아집니다.
  • const Color(...)처럼 생성자에도 const를 붙여야 완전히 불변으로 취급됩니다.

비교 예시:

1
2
3
4
5
// Before (non-const)
SizedBox(height: 120);

// After (const 적용)
const SizedBox(height: 120);

시각화:

1
2
3
4
5
┌─────────────────────────────┐
│ Total Balance │
│ $5 194 482 │
│ [ Transfer ] │
└─────────────────────────────┘

🎨 화면 미리보기

1
잔액 텍스트 아래 노란색 Transfer 버튼이 나타난 모습

📊 동작 흐름

1
2
3
4
5
1. Column이 잔액 텍스트와 버튼을 위→아래로 배치
2. SizedBox들이 각 요소 사이 간격을 확보
3. Row가 버튼을 가로 방향으로 감싸며 향후 추가 버튼을 대비
4. Container+Padding 조합으로 버튼 형태를 완성
5. const 키워드가 불필요한 재빌드를 막아 성능을 지킴

✅ 체크리스트

  • Total Balance 텍스트가 살짝 회색빛으로 표현되는가?
  • 잔액 숫자가 굵고 크게 표시되는가?
  • 버튼이 둥근 사각형 형태로 렌더링되는가?
  • 변하지 않는 위젯에 const가 붙어 있는가?

💡 연습 과제

  1. Request 버튼을 추가로 만들어 Row 안에 나란히 배치해보세요.
  2. 버튼 색상을 다른 브랜드 컬러로 바꿔 보고 어울리는지 확인하세요.
  3. const를 붙일 수 있는 다른 위젯이 없는지 찾아보고 적용하세요.

재사용 가능한 버튼 위젯 추출

🎯 이번 단계에서 배울 것

  • 위젯을 별도 파일로 분리해 재사용성을 높이기
  • 생성자 파라미터에 requiredfinal을 사용해 안전한 위젯 설계하기
  • main.dart에서 커스텀 위젯 두 개를 서로 다른 테마로 배치하기

📂 파일 구조

1
2
3
4
5
새로 생성되는 파일
- lib/widgets/button.dart (커스텀 버튼 위젯)

수정되는 파일
- lib/main.dart (Button 위젯 사용 및 padding, 간격 조정)

📝 1단계: Button 위젯 정의하기

전체 코드 (lib/widgets/button.dart @ 2.5):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import 'package:flutter/material.dart';

class Button extends StatelessWidget {
final String text;
final Color bgColor;
final Color textColor;

const Button({
super.key,
required this.text,
required this.bgColor,
required this.textColor,
});

@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(45),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 20,
horizontal: 50,
),
child: Text(
text,
style: TextStyle(
color: textColor,
fontSize: 20,
),
),
),
);
}
}

🔍 코드 상세 설명

1. StatelessWidget으로 재사용성 확보

  • 버튼 내용(text)과 스타일(bgColor, textColor)을 파라미터로 받아 다양한 조합을 지원합니다.
  • 상태 변화가 없으므로 StatelessWidget이 적합하며, 불필요한 상태 관리를 피할 수 있습니다.

2. requiredfinal

  • 생성자에 required를 추가해 필수 인수를 누락하면 컴파일 시점에 바로 오류가 납니다.
  • 멤버 변수를 final로 선언해 위젯이 불변이라는 것을 명시합니다.

3. 기존 Container 코드 재사용

  • 2.2에서 작성한 버튼 스타일을 그대로 옮겨 왔기 때문에 UI가 변하지 않습니다.
  • padding도 함께 포함하여 호출하는 쪽에서 반복할 필요가 없습니다.

📝 2단계: main.dart에서 Button 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Button(
text: 'Transfer',
bgColor: Color(0xFFF1B33B),
textColor: Colors.black,
),
Button(
text: 'Request',
bgColor: Color(0xFF1F2123),
textColor: Colors.white,
),
],
),
  • RowmainAxisAlignment.spaceBetween으로 두 버튼을 좌우 끝에 배치했습니다.
  • 같은 위젯이라도 색 조합만 바꿔 완전히 다른 느낌을 줄 수 있음을 보여줍니다.

비교 예시:

1
2
3
4
5
// Before
Container(...); // Transfer

// After
const Button(text: 'Transfer', ...);

🎨 화면 미리보기

1
[ Transfer ]        [ Request ]  ← 동일한 버튼 위젯의 두 가지 스타일

📊 동작 흐름

1
2
3
4
1. main.dart가 Button 클래스를 임포트
2. Row 안에서 Button을 두 번 호출하며 다른 속성을 전달
3. Button이 텍스트/색상을 받아 Container를 렌더링
4. 동일한 구조를 복제하지 않고도 UI 일관성을 유지

✅ 체크리스트

  • lib/widgets/button.dart 파일이 생성되었는가?
  • Button 위젯의 모든 인수가 required로 선언되었는가?
  • main.dart에서 두 버튼 색상이 정확히 다르게 표시되는가?

💡 연습 과제

  1. Button 위젯에 icon 파라미터를 추가하고 아이콘이 있는 버튼을 만들어보세요.
  2. 버튼 너비를 double 파라미터로 받아 다양한 길이를 지원하게 확장해보세요.
  3. GestureDetector를 감싸 클릭 이벤트를 받을 준비를 해보세요.

월렛 카드 UI 구성

🎯 이번 단계에서 배울 것

  • 둥근 카드 컨테이너 안에 텍스트를 세로로 배치하기
  • crossAxisAlignment.endspaceBetween으로 제목/바로가기 배치하기
  • 통화 금액과 단위를 나란히 배치하는 Row 구성 이해하기

📂 파일 구조

1
2
수정되는 파일
- lib/main.dart (Wallets 섹션과 첫 번째 카드 추가)

📝 1단계: Wallets 타이틀과 카드 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
const SizedBox(height: 100),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Wallets',
style: TextStyle(
color: Colors.white,
fontSize: 36,
fontWeight: FontWeight.w600,
),
),
Text(
'View All',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 18,
),
),
],
),
const SizedBox(height: 20),
Container(
decoration: BoxDecoration(
color: const Color(0xFF1F2123),
borderRadius: BorderRadius.circular(25),
),
child: Padding(
padding: const EdgeInsets.all(30),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Euro',
style: TextStyle(
color: Colors.white,
fontSize: 32,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 10),
Row(
children: [
const Text(
'6 428',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
const SizedBox(width: 5),
Text(
'EUR',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 20,
),
),
],
)
],
),
],
),
),
),

🔍 코드 상세 설명

1. 섹션 헤더 배치

  • crossAxisAlignment.endWallets 제목과 View All을 baseline에 맞춰 자연스러운 시선을 유도합니다.
  • View AllwithOpacity로 살짝 흐려 보조 정보임을 표현합니다.

2. 카드 컨테이너

  • 배경색을 한 단계 밝게(0xFF1F2123) 설정해 메인 배경과 구분했습니다.
  • borderRadius.circular(25)로 부드러운 카드 모양을 만들었습니다.

3. 카드 내부 텍스트

  • Column 안 첫 줄은 통화 이름, 두 번째 Row에는 금액과 단위를 나란히 배치합니다.
  • 금액과 단위 사이에 SizedBox(width: 5)로 적당한 간격을 둡니다.

시각화:

1
2
3
4
5
Wallets                View All
┌──────────────────────────────┐
│ Euro │
│ 6 428 EUR │
└──────────────────────────────┘

🎨 화면 미리보기

1
카드 형태 컨테이너 안에 통화 정보가 정돈된 모습

📊 동작 흐름

1
2
3
4
1. Row가 섹션 제목과 View All 버튼을 좌우로 배치
2. SizedBox로 섹션과 카드 사이 여백 확보
3. Container가 카드 배경과 모서리를 렌더링
4. Column→Row 조합으로 통화명과 금액/코드를 위→아래, 좌→우로 배치

✅ 체크리스트

  • Wallets 제목과 View All이 같은 라인에 정렬되는가?
  • 카드 배경색이 메인 배경보다 밝게 표시되는가?
  • 금액과 통화 코드가 한 줄에 나란히 보이는가?

💡 연습 과제

  1. 다른 통화를 하나 더 추가하고 카드 아래에 배치해보세요.
  2. 카드 배경색을 살짝 더 밝은 회색으로 바꿔 대비를 실험해보세요.
  3. View All 텍스트를 버튼으로 만들고 onPressed 콜백을 준비해보세요.

아이콘 배치와 Transform 활용

🎯 이번 단계에서 배울 것

  • clipBehavior: Clip.hardEdge로 자식이 카드 바깥으로 넘치지 않게 하기
  • Transform.scale, Transform.translate를 조합해 아이콘 위치와 크기 조정하기
  • 머티리얼 아이콘(Icons.euro_rounded)을 활용해 시각적 포인트 주기

📂 파일 구조

1
2
수정되는 파일
- lib/main.dart (Euro 카드에 아이콘 추가)

📝 1단계: 카드에 통화 아이콘 얹기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: const Color(0xFF1F2123),
borderRadius: BorderRadius.circular(25),
),
child: Padding(
padding: const EdgeInsets.all(30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// ... 왼쪽 텍스트 컬럼 ...
Transform.scale(
scale: 2.2,
child: Transform.translate(
offset: const Offset(-5, 12),
child: const Icon(
Icons.euro_rounded,
color: Colors.white,
size: 88,
),
),
)
],
),
),
),

🔍 코드 상세 설명

1. Clip 설정

  • clipBehavior: Clip.hardEdge로 둥근 둥근 모서리 밖으로 아이콘이 튀어나오지 않게 자릅니다.

2. Transform 두 번 중첩

  • Transform.scale로 아이콘을 2.2배 확대합니다.
  • 내부에서 Transform.translate를 적용해 살짝 왼쪽(-5)과 아래(+12)로 이동시킵니다.
  • WYSIWYG처럼 아이콘 위치를 세밀하게 조정할 수 있습니다.

3. 아이콘 스타일

  • size: 88과 흰색을 사용해 어두운 배경 위에서 눈에 띄게 만듭니다.
  • 통화별로 대표 아이콘을 사용하면 직관적인 UI가 됩니다.

🎨 화면 미리보기

1
2
3
4
┌──────────────────────────────┐
│ Euro € (확대 아이콘)│
│ 6 428 EUR │
└──────────────────────────────┘

📊 동작 흐름

1
2
3
4
1. Row가 텍스트와 아이콘을 양쪽 끝으로 분리
2. Transform.scale이 아이콘을 크게 확대
3. Transform.translate가 살짝 위치를 조정
4. Clip.hardEdge가 카드 밖으로 나간 부분을 잘라서 깔끔하게 유지

✅ 체크리스트

  • 아이콘이 카드 밖으로 삐져나오지 않는가?
  • 아이콘이 텍스트와 자연스럽게 맞춰졌는가?
  • Transform 적용 순서(scale → translate)가 올바른가?

💡 연습 과제

  1. 아이콘 색상을 Colors.amber로 바꿔 카드 강조 색을 실험해보세요.
  2. Transform.rotate를 추가해 아이콘을 살짝 기울여 보세요.
  3. 다른 통화 아이콘을 찾아 아이콘 데이터를 교체해보세요.

CurrencyCard 위젯과 스크롤 처리

🎯 이번 단계에서 배울 것

  • SingleChildScrollView로 세로 레이아웃을 스크롤 가능하게 만들기
  • 카드 구성을 CurrencyCard 위젯으로 분리해 재사용성 극대화하기
  • isInverted 플래그로 밝은 카드/어두운 카드 테마 전환 구현하기
  • Transform.translate로 카드 간 겹쳐 보이게 오프셋 주기

📂 파일 구조

1
2
3
4
5
새로 생성되는 파일
- lib/widgets/currency_card.dart (재사용 가능한 카드 위젯)

수정되는 파일
- lib/main.dart (스크롤 뷰 도입 및 카드 세 개 배치)

📝 1단계: CurrencyCard 위젯 만들기

전체 코드 (lib/widgets/currency_card.dart @ 2.8):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import 'package:flutter/material.dart';

class CurrencyCard extends StatelessWidget {
final String name, code, amount;
final IconData icon;
final bool isInverted;

final _blackColor = const Color(0xFF1F2123);

const CurrencyCard({
super.key,
required this.name,
required this.code,
required this.amount,
required this.icon,
required this.isInverted,
});

@override
Widget build(BuildContext context) {
return Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: isInverted ? Colors.white : _blackColor,
borderRadius: BorderRadius.circular(25),
),
child: Padding(
padding: const EdgeInsets.all(30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
name,
style: TextStyle(
color: isInverted ? _blackColor : Colors.white,
fontSize: 32,
fontWeight: FontWeight.w600,
),
),
const SizedBox(
height: 20,
),
Row(
children: [
Text(
amount,
style: TextStyle(
color: isInverted ? _blackColor : Colors.white,
fontSize: 20,
),
),
const SizedBox(
width: 5,
),
Text(
code,
style: TextStyle(
color: isInverted
? _blackColor
: Colors.white.withOpacity(0.8),
fontSize: 20,
),
),
],
)
],
),
Transform.scale(
scale: 2.2,
child: Transform.translate(
offset: const Offset(
-5,
12,
),
child: Icon(
icon,
color: isInverted ? _blackColor : Colors.white,
size: 88,
),
),
)
],
),
),
);
}
}

🔍 코드 상세 설명

1. 테마 전환 플래그 isInverted

  • 카드 배경과 텍스트, 아이콘 색을 조건문 없이 삼항 연산자로 깔끔히 바꿉니다.
  • 밝은 카드와 어두운 카드를 동일한 위젯이 담당하게 되어 중복이 줄었습니다.

2. _blackColor 필드

  • 반복적으로 사용하는 0xFF1F2123 색상을 필드로 빼 재사용합니다.
  • 밑줄로 시작하는 변수명은 Dart에서 private(파일 내 전용)을 의미합니다.

3. 기존 Transform 로직 재사용

  • 2.7에서 만든 아이콘 배치를 그대로 포함해 어느 카드에서나 적용됩니다.

📝 2단계: 스크롤 뷰와 카드 배치하기

핵심 변경 (lib/main.dart @ 2.8):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
body: SingleChildScrollView(
child: Padding(
// ...
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ... 기존 헤더/버튼 ...
const CurrencyCard(
name: 'Euro',
code: 'EUR',
amount: '6 428',
icon: Icons.euro_rounded,
isInverted: false,
),
Transform.translate(
offset: const Offset(0, -20),
child: const CurrencyCard(
name: 'Bitcoin',
code: 'BTC',
amount: '9 785',
icon: Icons.currency_bitcoin,
isInverted: true,
),
),
Transform.translate(
offset: const Offset(0, -40),
child: const CurrencyCard(
name: 'Dollar',
code: 'USD',
amount: '428',
icon: Icons.attach_money_outlined,
isInverted: false,
),
),
],
),
),
),
  • SingleChildScrollView로 긴 화면에서 overflow 에러 없이 세로 스크롤이 되도록 했습니다.
  • Transform.translate를 카드마다 적용해 살짝 겹치는 느낌의 스택 효과를 냅니다.
  • 두 번째 카드만 isInverted: true를 사용해 흰색 카드 역색상을 보여줍니다.

🎨 화면 미리보기

1
2
3
Euro Card
↳ Bitcoin Card (흰색, 살짝 위로 겹침)
↳ Dollar Card (다시 어두운 테마, 더 위로 겹침)

📊 동작 흐름

1
2
3
4
1. SingleChildScrollView가 Column 전체를 스크롤 가능한 영역으로 감쌈
2. CurrencyCard 위젯이 전달받은 데이터로 카드 하나를 렌더링
3. isInverted bool이 카드 색상 테마를 결정
4. Transform.translate가 각 카드의 Y값을 음수로 이동시켜 겹쳐 보이게 함

✅ 체크리스트

  • 스크롤이 필요한 화면에서 overflow 에러가 사라졌는가?
  • 세 가지 카드가 서로 다른 색/데이터로 표시되는가?
  • Transform 덕분에 카드가 계단식으로 겹쳐 보이는가?

💡 연습 과제

  1. isInverted를 추가 카드에 적용하며 밝은/어두운 테마를 자유롭게 조합해보세요.
  2. Transform.translate의 offset 값을 조절해 카드 겹침 간격을 바꿔보세요.
  3. CurrencyCardorder 파라미터를 만들어 자동으로 높이 오프셋을 결정하도록 개선해보세요.

Code Challenge 반영

🎯 이번 단계에서 배울 것

  • 마지막 카드 아이콘을 다른 머티리얼 아이콘으로 교체하기
  • 작은 변경도 깃 커밋으로 남겨 추적 가능하게 만들기

📂 파일 구조

1
2
수정되는 파일
- lib/main.dart (USD 카드 아이콘 교체)

📝 1단계: Dollar 카드 아이콘 변경

1
2
3
4
5
6
7
8
9
10
Transform.translate(
offset: const Offset(0, -40),
child: const CurrencyCard(
name: 'Dollar',
code: 'USD',
amount: '428',
icon: Icons.monetization_on_outlined,
isInverted: false,
),
),
  • Icons.attach_money_outlined에서 Icons.monetization_on_outlined로 교체해 과제 요구사항을 반영했습니다.
  • 머티리얼 아이콘 라이브러리의 다양한 아이콘을 탐색하는 계기가 됩니다.

🎨 화면 미리보기

1
Dollar 카드 아이콘이 세로 동전 그래픽으로 바뀐 모습

📊 동작 흐름

1
2
3
1. CurrencyCard가 새로운 아이콘 데이터를 전달받음
2. Icon 위젯이 해당 심볼을 렌더링
3. 나머지 레이아웃은 기존과 동일하게 유지

✅ 체크리스트

  • Dollar 카드 아이콘이 바뀌었는가?
  • 나머지 카드 레이아웃에는 영향이 없는가?

💡 연습 과제

  1. 다른 통화 카드도 각기 어울리는 아이콘으로 바꿔보세요.
  2. 아이콘 색상을 카드 테마와 다르게 줘서 시각적 대비를 확인하세요.
  3. 머티리얼 아이콘 사이트에서 즐겨 쓰는 아이콘을 찾아 목록을 정리해보세요.

📚 전체 흐름 정리

  • 다크 테마 배경과 헤더가 완성되었습니다.
  • 잔액 정보와 CTA 버튼을 추가하고 const로 렌더링을 최적화했습니다.
  • 버튼을 별도 위젯으로 추출해 재사용성을 높였습니다.
  • Wallets 섹션과 통화 카드, 아이콘 배치를 마무리했습니다.
  • 카드 역시 컴포넌트화하고 스크롤 처리, 겹침 효과를 구현했습니다.
  • 코드 챌린지에서는 아이콘 교체로 머티리얼 컴포넌트 탐색을 마쳤습니다.

✅ 최종 점검 리스트

  • 헤더, 잔액, Wallets 섹션이 순서대로 화면에 배치되었는가?
  • Button과 CurrencyCard 위젯이 각각 lib/widgets 폴더에 존재하는가?
  • isInverted가 카드 색상 테마를 잘 전환하고 있는가?
  • 스크롤이 필요한 경우 SingleChildScrollView가 적용되어 있는가?
  • 최종 아이콘 교체가 반영되었는가?

출처 : https://nomadcoders.co/flutter-for-beginners